首页 > 其他分享 >学习笔记:二分图

学习笔记:二分图

时间:2023-10-28 16:12:14浏览次数:48  
标签:二分 cnt 匹配 int head 笔记 学习 集合

二分图

引入

二分图又被称为二部图。

二分图就是可以二分答案的图。

二分图是节点由两个集合组成,且两个集合内部没有边的图。换言之,存在一种方案,将节点划分成满足以上性质的两个集合。

性质

如果两个集合中的点分别染成黑色和白色,可以发现二分图中的每一条边都一定是连接一个黑色点和一个白色点。换言之,二分图中不存在奇环。

证明:

​ 因为每一条边都是从一个集合走到另一个集合,只有走偶数次才可能回到同一个集合。

证毕。

判定

可以直接暴力枚举集合方案。

可以根据二分图性质来判断。考虑 dfs 或者 bfs 遍历整张图,如果图中不存在奇环则证明是二分图,反之则不是。

或者也可以考虑染色。如果存在冲突则证明不是二分图。

#include <iostream>
#include <cstring>
#define MAXN 100005
#define MAXM 200005
using namespace std;
int n, m, u, v, w;
struct edge{int to, nxt;}e[MAXM << 1];
int head[MAXN], cnt = 1;
int col[MAXN];
bool flag = true;
int read(){
    int t = 1, x = 0;char ch = getchar();
    while(!isdigit(ch)){if(ch == '-')t = -1;ch = getchar();}
    while(isdigit(ch)){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
    return x * t;
}
void write(int x){
    if(x < 0){putchar('-');x = -x;}
    if(x >= 10)write(x / 10);
    putchar(x % 10 ^ 48);
}
void add(int u, int v){
    cnt++;e[cnt].to = v;e[cnt].nxt = head[u];head[u] = cnt;
    cnt++;e[cnt].to = u;e[cnt].nxt = head[v];head[v] = cnt;
}
void dfs(int now, int color){
    for(int i = head[now] ; i != 0 ; i = e[i].nxt){
        if(flag == false)return;
        int v = e[i].to;
        if(col[v] == -1)
            col[v] = color,dfs(v, color ^ 1);
        else if(col[v] != color){
            flag = false;return;
        }
    }
}
int main(){
    n = read();m = read();
    for(int i = 1 ; i <= m ; i ++)
        u = read(),v = read(),add(u, v);
    memset(col, -1, sizeof(col));dfs(1, 1);
    if(flag == true)puts("Yes");
    else puts("No");
    return 0;
}

二分图最大匹配

匹配:在图论中,一个「匹配」是一个边的集合,其中任意两条边都没有公共顶点。

解释(不正经版):可以把二分图左右两个集合分别想象成男生女生,匹配的过程就像是在相亲,最大匹配就是寻找一个方案能够相成最多对。

可以考虑将该问题转换成网络流问题来解决。

具体地,将源点连上左边所有点,右边所有点连上汇点,容量皆为 \(1\)。原来的每条边从左往右连边,容量也皆为 \(1\),最大流即最大匹配。

如果使用Dinic 算法求该网络的最大流,可在 \(O(\sqrt{n}m)\) 求出。

洛谷 P3386【模板】二分图最大匹配

题目描述

给定一个二分图,其左部点的个数为 \(n\),右部点的个数为 \(m\),边数为 \(e\),求其最大匹配的边数。

左部点从 \(1\) 至 \(n\) 编号,右部点从 \(1\) 至 \(m\) 编号。

输入格式

输入的第一行是三个整数,分别代表 \(n\),\(m\) 和 \(e\)。

接下来 \(e\) 行,每行两个整数 \(u, v\),表示存在一条连接左部点 \(u\) 和右部点 \(v\) 的边。

输出格式

输出一行一个整数,代表二分图最大匹配的边数。

#include <iostream>
#include <cstring>
#include <queue>
#define MAXN 1005
#define MAXM 102005
using namespace std;
int n, m, t, u, v, w;
struct edge{int w, to, nxt;}e[MAXM];
int dep[MAXN], rad[MAXN], head[MAXN], cnt = 1;
queue <int> q;
int read(){
    int t = 1, x = 0;char ch = getchar();
    while(!isdigit(ch)){if(ch == '-')t = -1;ch = getchar();}
    while(isdigit(ch)){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
    return x * t;
}
void add(int u, int v, int w){
    cnt++;e[cnt].w = w;e[cnt].to = v;e[cnt].nxt = head[u];head[u] = cnt;
}
bool bfs(){
    while(!q.empty())q.pop();
    memset(dep, 0, sizeof(dep));
    dep[0] = 1;q.push(0);
    while(!q.empty()){
        int u = q.front();q.pop();
        rad[u] = head[u];
        for(int i = head[u] ; i != 0 ; i = e[i].nxt){
            if(dep[e[i].to] == 0 && e[i].w != 0){
                dep[e[i].to] = dep[u] + 1;q.push(e[i].to);
                if(e[i].to == n + m + 1)return true;
            }
        }
    }
    return false;
}
int dfs(int now, int flow){
    if(now == n + m + 1)return flow;int tmp = flow;
    for(int i = rad[now] ; i != 0 ; i = e[i].nxt){
        rad[now] = i;
        if(dep[e[i].to] == dep[now] + 1 && e[i].w != 0){
            int k = dfs(e[i].to, min(tmp, e[i].w));
            if(k == 0)dep[e[i].to] = 0;
            e[i].w -= k;e[i ^ 1].w += k;tmp -= k;
        }
    }
    return flow - tmp;
}
int dinic(){
    int ans = 0;
    while(bfs() == true)
        ans += dfs(0, 1e18);
    return ans;
}
int main(){
    n = read();m = read();t = read();
    for(int i = 1 ; i <= t ; i ++){
        u = read();v = read();
        add(u, n + v, 1);add(n + v, u, 0);
    }
    for(int i = 1 ; i <= n ; i ++)add(0, i, 1),add(i, 0, 0);
    for(int i = 1 ; i <= m ; i ++)add(n + i, n + m + 1, 1),add(n + m + 1, n + i, 0);
    cout << dinic() << endl;return 0;
}

二分图最大权匹配

一样的思路,转化为费用流模型。

标签:二分,cnt,匹配,int,head,笔记,学习,集合
From: https://www.cnblogs.com/tsqtsqtsq/p/17794201.html

相关文章

  • 学习笔记:拓扑排序
    拓扑排序引入拓扑排序是一个有向无环图的所有顶点的线性序列。该序列需要满足每个顶点出现且只出现一次和如果有一条AA到BB的路径,在序列中AA出现在BB的前面。实现拓扑排序的步骤:计算每个点的入度。入度为\(0\)就加入队列。当队列不为空则循环:取出队首元素并......
  • javaweb学习每日总结-第八天
    第八天学习Springboot今天也终于是学到了springboot的技术,springboot是一款Java开发的框架,也是当下最流行的开发方式,没有之一!今天我进行了springboot技术的入门,初步了解了springboot技术的发展和应用,也用idea写了一个最简单的springboot程序。除此之外,我还下载了postmen这个软......
  • 学习笔记7
    第三章第四章并发编程并行计算并行计算是一种计算方案,它尝试使用多个执行并行算法的处理器更快速的解决问题顺序算法与并行算法并行性与并发性并行算法只识别可并行执行的任务。CPU系统中,并发性是通过多任务处理来实现的线程线程的原理某进程同一地址空间上的独立执行......
  • yzy第七周学习笔记
    第四章并发编程4.1并行计算导论Linux环境中有很多应用程序和很多进程,其中最重要的是客户端网络/服务器。多进程服务器是指当客户端发出请求时,服务器使用子进程来处理客户端的请求。父进程继续等待来自其他客户端的请求。这种方法的优点是服务器可以在客户端请求时管理客户......
  • 数据结构与算法(LeetCode)第一节:认识复杂度,对数器,二分法与异或运算
    一、认识复杂度1.评估算法优劣的核心指标:时间复杂度:当完成了表达式的建立,只要把最高阶项留下即可。低阶项都去掉,高阶项的系数也去掉,记为O(去掉系数的高阶项);​ 时间复杂度是衡量算法流程的复杂度的一种指标,该指标只与数据量有关,与过程之外的优化无关常见的时间复杂度(从好到坏)O......
  • php代码审计学习----蜜蜂cms代码审计
    php代码审计学习----蜜蜂cms代码审计源码https://github.com/Betsy0/CMSVulSource/tree/main/beescms环境搭建这个需要用docker搭建环境用windows的phpstudy会出现403然后chmod-R777html在docker容器里mysql-uroot-prootcreatedatabasebeescms;然后再/etc/mysq......
  • 学习笔记七
    学习笔记七一、作业要求自学教材第4章,提交学习笔记(10分),评分标准如下知识点归纳以及自己最有收获的内容,选择至少2个知识点利用chatgpt等工具进行苏格拉底挑战,并提交过程截图,提示过程参考下面内容(4分)“我在学***X知识点,请你以苏格拉底的方式对我进行提问,一次一个问题”核......
  • 阅读笔记5
    领域驱动设计的最佳实践领域驱动设计(DDD)有一些最佳实践,可以帮助您更好地应对软件核心复杂性:建模与沟通:建立共享的领域模型,确保开发团队和领域专家之间的共同理解。使用通用语言来描述领域对象和操作。持续演化:领域模型是一个持续演化的过程。随着对业务需求的深入了解,不断改进和......
  • uboot中am335x的relocate分析--Apple的学习笔记
    一,前言今天我主要先分析下bbblack的relocate。至于为什么要分析这块内容,因为我个人理解,内存分布也是重要内容,最关键的是这些内容我3年前分析过TQ2440的,但是没分析过bbblack的,所以补上。二,实践先在board_f.c中添加#define_DEBUG1就支持debug函数打印信息了。U-Boot2023.10(Oc......
  • 2023-2024-1 20211306 密码系统设计与实现课程学习笔记7
    20211306密码系统设计与实现课程学习笔记7任务详情自学教材第4章,提交学习笔记知识点归纳以及自己最有收获的内容,选择至少2个知识点利用chatgpt等工具进行苏格拉底挑战,并提交过程截图,提示过程参考下面内容“我在学***X知识点,请你以苏格拉底的方式对我进行提问,一次一个问题......