二分图
二分图的定义:
如果一个图是二分图,那么图中所有的点都能够划分到两个集合中,使得点与点之前的边都在两个集合之间,集合内部没有边。
二分图的重要性质
1):二分图当且仅当图中不含奇数环。
假设一个环中有n个点,如果n是奇数,那么第1个顶点和第n个顶点肯定会被加入到同一个集合中,即集合内部有边相连,则无法构成二分图;如果n是偶数,那么第1个顶点和第n个顶点肯定会被加入到不同的集合中,即点与点之间的边在两个集合之间,满足二分图的定义,能够构成二分图
2)能够二染色
即把二分图中所有顶点都染成两种颜色,且同一条边相连的顶点颜色不同。
通过染色法判断一个图是否是二分图:
时间复杂度:O(n + m)
思路:
通过对一个图中的每个点和其连接着的点进行染色,如果出现相连的点的颜色相同,即出现矛盾,说明该图无法构成二分图;如果可以将整个图都染完色且不出现矛盾,则该图是二分图。
染色法代码:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 1e5 + 5;
vector<int>h[N]; //通过动态数组模拟邻接表
int n, m, color[N]; //color数组:记录某个顶点是否被染色
//c:当前染色的编号 1 或 2
int dfs(int u , int c){
color[u] = c;
for(int i = 0; i < h[u].size(); i ++){
int& j = h[u][i];
if(!color[j]){
if(!dfs(j, 3 - c))return 0; //染色失败,递归返回0
}
else if(color[j] == c)return 0; //相邻点同色情况,表明染色出现矛盾,无法构成二分图
}
return 1;
}
int main()
{
cin>>n>>m;
for(int i = 1; i <= m; i ++){
int a, b;
cin>>a>>b;
h[a].push_back(b);
h[b].push_back(a);
}
int flag = 1; //判断染色是否合法
for(int i = 1; i <= n; i ++){
if(!color[i]){
if(!dfs(i, 1))flag = 0;
}
}
if(flag)cout<<"Yes"<<'\n';
else cout<<"No"<<'\n';
return 0;
}
匈牙利算法求二分图最大匹配值:
时间复杂度:O(nm)
思路:
设二分图的两个集合为n1和n2.
让n1中顶点u与n2中没有匹配顶点j匹配,如果n1顶点u找到的n2的顶点j匹配过了,就去看能否让与n2的顶点j匹配的n1中的顶点i匹配其他的顶点。如果匹配成功就让匹配数加一,最后输出匹配数即可。
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 505;
vector<int>h[N]; //通过动态数组模拟邻接表
int st[N], match[N]; //st:是否找过某个n2的值 match:记录n2中某个值匹配的n1的值
int n1, n2, m;
//find函数:能否为u在n2中找到匹配
int find(int u){
for(int i = 0; i < h[u].size(); i ++) //遍历与u相连的n2中的顶点
{
int& j = h[u][i];
if(!st[j]){
st[j] = 1; //令当前点匹配过
if(match[j] == 0 || find(match[j])) //如果该点j没有被匹配过,或者与之匹配的n1中的点可以找其他n2中的点进行匹配,那么我们就让顶点j与当前顶点u匹配
{
match[j] = u;
return true;
}
}
}
return false; //顶点u无法匹配n2中的任何一个顶点
}
int main()
{
//n1:集合1的顶点数量
//n2:集合2的顶点数量
//m:集合1和集合2之间顶点相连的边数
cin >> n1 >> n2 >> m;
for(int i = 1; i <= m; i ++){
int u, v;
cin >> u >> v;
h[u].push_back(v);
}
int res = 0;
for(int i = 1; i <= n1; i ++){
memset(st, 0, sizeof(st)); //初始化:让所有n2中的顶点都没有匹配过
if(find(i))res ++; //i匹配成功
}
cout << res << '\n';
return 0;
}
/*
st数组作用:
1)防止当u指向一个被匹配过的顶点j的时候,让匹配该顶点j的顶点i重复找到该点j造成递归死循环
2)st[j]表示当前u找过该顶点j
*/