前言:今天来记图论中二分图的问题,二分图会是很多图论中的盲点,这里记两个关于二分图的算法
目录
1、染色法判段二分图
思路:染色法的核心思想为将当前节点染成一个颜色,然后将他连接的边全部染成另一个颜色。
如果节点中存在奇数环,就会存在冲突,即要染成不同颜色的点和当前节点相同颜色。
代码模板:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e5+10,M=2e5+10;
int h[N],e[M],ne[M],idx;//数组模拟链表
int color[N];//当前节点颜色,0代表未染色
int n,m;
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;//数组模拟链表插入操作
}
bool dfs(int u,int c){
color[u]=c;//将u节点染成c色
for(int i=h[u];i!=-1;i=ne[i]){//遍历数组
int j=e[i];
if(!color[j]){//如果当前节点没有染色
if(!dfs(j,3-c)) return false;//如果染色失败
}
else if(color[j]==c) return false;//如果要染色的点和当前节点相同颜色
}
return true;
}
int main(){
scanf("%d%d",&n,&m);
memset(h,-1,sizeof h);//头结点初始化为-1
while(m--){
int a,b;
scanf("%d%d",&a,&b);
add(a,b),add(b,a);//将两个方向添加
}
bool flag=true;
for(int i=1;i<=n;i++){
if(!color[i]){//遍历如果当前节点没有染色
if(!dfs(i,1)){
flag=false;
break;
}
}
}
if(flag) puts("Yes");
else puts("No");
return 0;
}
2、匈牙利算法
题目模板:给定一个二分图,n1代表左边节点,n2代表右边的结点,m为边数,求左边和右边的最大匹配数。
思路:匈牙利算法的思路就很有意思了,如果左边的a和右边的m结点匹配了,a还能和右边的n匹配,遍历到下一个结点b的时候,发现b只能和m匹配,b就去问a,a能不能把m让给他,和n匹配,多么像绿帽子算法,好一个资源最大化!
上代码!
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=510,M=1e5+10;
int h[N],e[M],ne[M],idx;//数组模拟链表
int match[N];//当前点的匹配对象
bool st[N];
int n1,n2,m;//n1为左边的结点,n2为右边的结点,m为边数
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
bool find(int x){
for(int i=h[x];i!=-1;i=ne[i]){
int j=e[i];
if(!st[j]){
st[j]=true;
if(!match[j]||find(match[j])){
match[j]=x;
return true;
}
}
}
return false;
}
int main(){
scanf("%d%d%d",&n1,&n2,&m);
memset(h,-1,sizeof h);
while(m--){
int a, b;
scanf("%d%d",&a,&b);
add(a,b);//因为这里只从n1往n2走,所以不用记反向
}
int res=0;//储存最大匹配数
for(int i=1;i<=n1;i++){
memset(st,false,sizeof st);
if(find(i)) res++;
}
cout<<res;
return 0;
}