二分图简介
二分图是一种图论模型,其特点在于可以将图内的点分进两个集合,而每个集合内部的点没有直接关联,常用的二分图模型有二分图匹配
二分图最大匹配
1. 几种实现方法
代码原题P3386 【模板】二分图匹配
1. 匈牙利算法
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 const int INF=1e9+7,MAXN=1e3+1,MAXM=1e6+1; 5 int N,M,E,ans; 6 int used[MAXN],match[MAXN]; 7 int head[MAXN],to[MAXM],nxt[MAXM],tp; 8 inline void add(int x,int y){ 9 nxt[++tp]=head[x]; 10 head[x]=tp; 11 to[tp]=y; 12 } 13 int dfs(int x){//x->cur vertex 14 for(int i=head[x];i;i=nxt[i]){ 15 if(!used[to[i]]){ 16 used[to[i]]=1; 17 if(!match[to[i]]||dfs(match[to[i]])){ 18 match[to[i]]=x; 19 return 1; 20 } 21 } 22 } 23 return 0; 24 } 25 int main(){ 26 scanf("%d%d%d",&N,&M,&E); 27 for(int i=1;i<=E;i++){ 28 int ii,jj; 29 scanf("%d%d",&ii,&jj); 30 if(ii<=N&&jj<=M){ 31 add(ii,jj); 32 } 33 } 34 for(int i=1;i<=N;i++){ 35 memset(used,0,sizeof(used)); 36 if(dfs(i)){ 37 ans++; 38 } 39 } 40 printf("%d",ans); 41 return 0; 42 }
2.最大流算法(此处是ISAP)
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 const int INF=1e9+7,MAXN=3e3+1,MAXM=2e6+1; 5 int N,M,E; 6 int head[MAXN],to[MAXM*2],nxt[MAXM*2],val[MAXM*2],tp=1; 7 inline void add(int x,int y,int z){ 8 nxt[++tp]=head[x]; 9 head[x]=tp; 10 to[tp]=y; 11 val[tp]=z; 12 nxt[++tp]=head[y]; 13 head[y]=tp; 14 to[tp]=x; 15 } 16 int S,T,de[MAXN],gap[MAXN]; 17 inline void bfs(){ 18 queue<int> que; 19 que.push(T); 20 memset(de,-1,sizeof(de)); 21 de[T]=0; 22 gap[0]=1; 23 while(que.size()){ 24 int idx=que.front(); 25 que.pop(); 26 for(int i=head[idx];i;i=nxt[i]){ 27 if(-1==de[to[i]]){ 28 de[to[i]]=de[idx]+1; 29 gap[de[to[i]]]++; 30 que.push(to[i]); 31 } 32 } 33 } 34 } 35 int maxflow,cur[MAXN]; 36 int dfs(int x,int flow){ 37 if(x==T){ 38 maxflow+=flow; 39 return flow; 40 } 41 int used=0; 42 for(int &i=cur[x];i;i=nxt[i]){ 43 if(val[i]&&de[to[i]]+1==de[x]){ 44 int ii=dfs(to[i],min(flow-used,val[i])); 45 if(ii){ 46 val[i]-=ii; 47 val[i^1]+=ii; 48 used+=ii; 49 if(used==flow){ 50 return used; 51 } 52 } 53 } 54 } 55 gap[de[x]]--; 56 if(!gap[de[x]]){ 57 de[S]=N+M+2; 58 } 59 de[x]++; 60 gap[de[x]]++; 61 return used; 62 } 63 inline void ISAP(){ 64 bfs(); 65 while(de[S]<=N+M+1){ 66 memcpy(cur,head,sizeof(cur)); 67 dfs(S,INF); 68 } 69 printf("%d",maxflow); 70 } 71 int main(){ 72 scanf("%d%d%d",&N,&M,&E); 73 S=0; 74 T=N+M+1; 75 for(int i=1;i<=N;i++){ 76 add(S,i,1); 77 } 78 for(int i=1;i<=M;i++){ 79 add(i+N,T,1); 80 } 81 for(int i=1;i<=E;i++){ 82 int ii,jj; 83 scanf("%d%d",&ii,&jj); 84 if(ii<=N&&jj<=M){ 85 add(ii,jj+N,1); 86 } 87 } 88 ISAP(); 89 return 0; 90 }
2. 简单应用
(1) 朴素的匹配问题
这一类问题的就是将一个实际问题转化为用一些对象匹配另一些对象的问题,难点在于建模,思维难度较高。这类问题大多数可以用匈牙利算法,代码短;也可以用网络流,代码长,效率高
例题:
洛谷P1640 【SCOI2010】连续攻击游戏(利用匈牙利算法的简单性质)
洛谷P1129 【ZJOI2007】矩阵游戏(巧妙的建模)
(2) 需要输出方案的匹配问题
洛谷P2526 [SHOI2001]小狗散步(匈牙利算法输出方案)
洛谷P2756 飞行员配对方案问题(Dinic算法)