二分图/二分图最大匹配(匈牙利算法)

        

二分图

  1. 定义

图为二分图当且仅当图中不存在奇数环

简单证明一下:

a08da34c2d424f10a493737ee3ffd6aa.png

 

 

我们把这些点分为两个集合 假设1为集合1 那么2为集合2 集合3为集合1  1为集合2

矛盾!!!所以不能出现奇数环

二分图的判定方法

dfs染色法

我们任意选择起点然后染其邻接点(邻接点染为相反颜色) 表示两个集合 如果出现矛盾那么就不是二分图(当前染的点的邻接点有颜色 但是和当前点的颜色相同 这就是矛盾) 用图模拟下:

 

607a7f63178d4f8398a8441ea2e5aa4c.png首先把任意一个点染为黑色 

 

874894ce69234020bbb25db421ebd976.png那么它的邻接点都是白色

29ea1fe7b2ee4ff7af3793175551c7da.png  然后白色邻接点都是黑色 这就是一个完美的二分图!!!

 

Code:

#include<bits/stdc++.h>

using namespace std;

const int N=10010;

int h[N],e[N],ne[N],color[N],idex,n,m;

void add(int a,int b){

e[idex]=b;

ne[idex]=h[a];

h[a]=idex++;

} //邻接表建立二分图

bool dfs(int x,int c){

color[x]=c;//c表示当前颜色

for(int i=h[x];i!=-1;i=ne[i]){//遍历其邻接点

if(!color[e[i]]){//如果没有被染色就染为相反的颜色

if(!dfs(e[i],3-c))return false;//邻接点染为相反颜色出现矛盾

}

else if(color[e[i]]==c)return false;//当前点出现矛盾

}

return true;

}

int main(){

memset(h,-1,sizeof(h));

cin>>n>>m;

int a,b;

while(m--){

cin>>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;

}

}

if(flag)cout<<"yes"<<endl;

else cout<<"no"<<endl;

}

应用

匈牙利最大匹配图:

因为二分图可以看成两个集合所以就可以把图看下图:

776e3c1e138144ef94aa1e2a85f21a2f.png

左为集合1右为集合2 那么二分图的匹配就是 左边集合的点对应一个右边集合的点 而且

右边集合只能对应一个左边的点(一一对应) 最大匹配就是左边最多几个点和左边的一一匹配

简单介绍一下匈牙利算法

从左边最上面的点开始加入左1匹配了右1到左2的时候2只能匹配右1 那么我们回溯到左1看左1是否有其他点可以匹配 如果有那么让左1匹配其他点 然后左2匹配右1(大体思路就是这样)

Code:

#include<bits/stdc++.h>

using namespace std;

const int N=510,M=100010;

int n1,n2,m;

int h[N],e[M],ne[M],idex;

int match[N];//记录右边的点匹配的是哪一个点

bool st[N];//标记当前点是否被考虑过;

void add(int a,int b){

e[idex]=b;

ne[idex]=h[a];

h[a]=idex++;

}

bool Find(int x){

for(int i=h[x];i!=-1;i=ne[i]){

if(!st[e[i]]){

st[e[i]]=true;

if(!match[e[i]]||Find(match[e[i]])){

match[e[i]]=x;

return true;

}

}

}

return false;

}

int main(){

cin>>n1>>n2>>m;

memset(h,-1,sizeof(h));

int a,b;

while(m--){

cin>>a>>b;

add(a,b);

}

int ans=0;

for(int i=1;i<=n1;i++){

memset(st,0,sizeof(st));

if(Find(i))ans++;

}

cout<<ans<<endl;

}

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值