【Step1】【二分图匹配】模版讲解
前言
二分图匹配问题是各类比赛的热门内容。涉及到的算法有:二分图最大匹配,二分图最佳匹配,König定理(最小覆盖问题)的证明与应用等等,但这些算法的根源在于匈牙利算法深透理解。本文只是简单的,通俗易懂的讲一讲二分图匹配使用匈牙利算法的模版,而理解了这个之后,就需要再去度娘那里看看其它的论文。
二分图
二分图,就是说在一个图中,将所有点分成两堆。如果所有的边的两个端点都分别在两堆中,这个图就是二分图。
二分图最大匹配
在所有的边中找若干的边,使得所选的边两两之间没有公共的端点。求最大的边数。
匈牙利算法模版
为了生动形象,我们将第一个集合每个点叫做公牛,另一个集合的点叫做母牛。这样这道题就转化成公牛找母牛的■■■■
首先,我们考虑贪心。如果公牛x和母牛y可以匹配,而且母牛y还没有匹配。那么直接匹配。
但是,如果公牛x和母牛y可以匹配但是母牛y和公牛z已经匹配。那么我们考虑这种情况:
如图,红边表示z和y连在一起。但是对于这四个点,我们明显可以找到一种更优的匹配。
我们发现,从第一种匹配的左下点开始,沿着:没选-选-没选的一条路径,将这条路径反向,我们的答案就能+1。
这样的(没选-选-没选……以没选开头和结束)路径叫做“增广路”,我们解决二分图匹配的过程,就是求增广路的过程。
bool find_muniu(int k) { for(int i=1;i<=m;i++) if (map[k][i]==true) if (chw[i]==true) { chw[i]=false; if ( match[i]==0 || find_muniu(match[i])==true ) { match[i]=k; return true; } } return false; }
我们每次枚举我们的公牛(k),然后让它去找匹配。
如果当前母牛没有被占据,就直接匹配。如果被占据,就让匹配了这头母牛的公牛再去找未匹配的母牛。(这个步骤就是找增广路的过程。)
唯一要注意的是chw数组,表示这个母牛在这次公牛的寻找匹配中有没有出现过。如果它本来就是增广路的一部分,我们走回来了,很明显不合法,而且会死循环。
这样子我们的代码就呼之欲出了
二分图匹配模版
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdio> #include<cstdlib> #include<cstring> int n,m; int first[205]; int len=0; bool map[205][205]; int match[205]; bool chw[205]; bool find_mn(int x) { for(int i=1;i<=m;i++) { if(map[x][i]==true && chw[i]==true) { chw[i]=false; if(match[i]==0 || find_mn(match[i])==true) { match[i]=x; return true; } } } return false; } int main() { while(scanf("%d %d",&n,&m)!=EOF) { memset(first,0,sizeof(first)); memset(map,false,sizeof(map)); len=0; for(int i=1;i<=n;i++) { int x; scanf("%d",&x); for(int j=1;j<=x;j++) { int soy; scanf("%d",&soy); map[i][soy]=true; } } int ans=0; memset(match,0,sizeof(match)); for(int i=1;i<=n;i++)//枚举公牛 { memset(chw,true,sizeof(chw)); if(find_mn(i)==true)ans++; } printf("%d\n",ans); } return 0; }
二分图的建图和对应的König定理,让这个专题变得很复杂。在之后的博客中再说吧。