二分图
把无向图分为两个集合,所有的边都在之间,而或的内部没有边。则该图成为称为二分图。
染色法判定二分图
一个图是否是二分图,一般用“染色法”进行判定,用两种颜色对所有顶点进行染色,要求一条边所连接的两个相邻顶点的颜色不同,染色结束后,如果所有相邻顶点的颜色都不相同,它就是二分图。
具体步骤:
- 初始时,每个节点颜色为0,代表未染色,顺序访问每个节点,判断节点i是否被染过色(1或2),若染过,则继续循环
- 若i未染色,将该节点颜色赋值为1(color[i] = 1),并遍历该节点的所有相邻节点
- 若相邻节点j未染色,则color[j] = 3 - color[i],并递归给j的相邻节点染色
- 若相邻节点已染色,判断其与节点i颜色是否相同,若相同,则发生冲突,即不是二分图
- 循环上述步骤,直到发生冲突或染色结束。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
using namespace std;
const int N = 1e5 + 10;
int h[N], ne[M], e[M], idx; //邻接表存图
int n, m, color[N]; //color取值0, 1, 2, 0代表未染色,1,2代表不同的颜色
void add(int a, int b){
e[idx] = b; ne[idx] = h[a]; h[a] = idx ++;
}
bool dfs(int u){
for (int i = h[u]; ~i; i = ne[i]) { //访问节点u的邻居,对其进行染色
int x = e[i];
if (!color[x]){
//若节点x未染色,则对其染色,并递归给其邻居节点染色
color[x] = 3 - color[u];
if (!dfs(x)) return false; //若给x的邻居进行染色时发生冲突,则返回false
}
else if (color[x] == color[u]) return false; //发生冲突,x颜色与邻居u一致,返回false
}
return true; //染色成功
}
int main(){
cin >> n >> m;
memset(h, -1, sizeof h);
int a, b;
for (int i = 0; i < m; i ++ ){
cin >> a >> b;
add(a, b), add(b, a); //添加边操作
}
bool ans = true;
for (int i = 1; i <= n; i ++ ){
if (!color[i]){ //若该节点未染色
color[i] = 1; //该节点颜色赋为1
if (!dfs(i)) { //若在给自己的邻居递归染色时发生冲突,则不是二分图,判定结束
ans = false;
break;
}
}
}
if (ans) puts("Yes");
else puts("No");
}
二分图的最大匹配,一般远小于
二分图的匹配:给定一个二分图 ,在 的一个子图 中, 的边集 中的任意两条边都不依附于同一个顶点,则称 是一个匹配。
二分图的最大匹配:所有匹配中包含边数最多的一组匹配被称为二分图的最大匹配,其边数即为最大匹配数。
给定一个二分图,其中左半部(假设是一群男生)包含 个点(编号 1∼),右半部(假设是一群女生)包含 个点(编号 1∼),二分图共包含 条边(表示两个人认识)。数据保证任意一条边的两个端点都不可能在同一部分中。每个人都想和自己认识的异性进行匹配,求最大匹配数。
匈牙利算法具体步骤:
- 使用match数组标记每个女生匹配成功的男生,st数组标记每个男生在进行匹配时访问的女生的状态,st[i]=true表示第i个女生该男生已经访问过了,false代表未访问
- 遍历每个男生,对每个男生i,访问其所有未访问的邻居(女生),对其邻居j,设置st[j] = true,即已访问过,若j未匹配,则设置match[j] = i.
- 若其邻居j已匹配,则递归j匹配成功的男生match[j],访问其所有为被男生i访问过的邻居,看其是否能匹配其他女生,若能,则将match[j]的值改为i,若不能,本次匹配失败。
- 循环上述步骤, 每成功匹配一次,结果+1,直到所有男生匹配结束
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 1e5 + 10;
int h[N], e[2 * N], ne[2 * N], idx; //邻接表存图
bool st[N]; // 访问状态,st[i] = true代表已访问过
int match[N], n1, n2, m; //match数组存放每个女生匹配到的男生
void add(int a, int b){
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
bool dfs(int u){ //对节点u(男生节点)进行匹配
for (int i = h[u]; ~i; i = ne[i]){
int x = e[i];
if (!st[x]){ //若x(女生节点)还未被访问过
st[x] = true; //无论匹配是否成功,均标记为已访问
if (!match[x]) { //若x还未匹配,则与u进行匹配,并返回true
match[x] = u;
return true;
}
else if (dfs(match[x])) { //若x已匹配,则判断其是否能与其它女生匹配(st数组在此处起作用),若能,则x与u也可匹配成功
match[x] = u;
return true;
}
}
}
return false; //匹配失败
}
int main(){
cin >> n1 >> n2 >> m; //两类节点的数量与边的数量
int a, b;
memset(h, -1, sizeof h);
for (int i = 0; i < m; i ++ ){
cin >> a >> b;
add(a, b);
}
int ans = 0;
for (int i = 1; i <= n1; i ++ ){
memset(st, 0, sizeof st); //因为不同男生之间存在重复邻居,但匈牙利算法要对每个男生访问其所有邻居进行匹配,因此,每次都需要重置st
if (dfs(i)) ans ++;
}
cout << ans << endl;
}