匈牙利算法是一种精妙的算法,我一开始都不敢相信他是对的。
先来看定义:
- 匹配 匹配是一个边集,其中任意两条边都没有公共端点。
- 最大匹配 顾名思义,包含边数最多的匹配
- 交错路 匹配边和非匹配边依次出现的一条路
- 增广路 从非匹配边出发,以非匹配边结束的交错路(增广路长度一定是奇数)
感觉好奇怪啊
匈牙利算法的基本思想,就是不断地扩大匹配的集合,不断尝试让匹配边数加一。匹配边数不能变多了,算法也就结束了。
有两条性质,乍一看很显然,其实很难证:
- 一个匹配(M)是最大匹配,当且仅当图中不存在增广路。
- 从某一点(左部点)出发,如果某个时刻找不到增广路,那就永远都找不到了。
性质1的必要性是显然的,让我们先来看算法的过程。
我们首先给二分图定向(说人话就是划分出左部和右部,规定增广路从左部出发)。枚举每一个左部点(是非匹配点),如果能找到增广路,我们就把增广路上所有边的状态取反,得到一种大小+1的匹配。
复杂度(O(nm)) (点数*边数)
结合代码理解一下
int vis[mxn],mch[mxn];//match
inline bool hungary(int x){
if(vis[x])return 0;vis[x]=1;
//vis记录左部点右部点皆可
for(int i=first[x];i;i=nxt[i]) //边是单向边
if(!mch[to[i]] || hungary(mch[to[i]]))return mch[to[i]]=x,1;
return 0;
}
//main函数
int ans=0;
for(int i=1;i<=n;++i)memset(vis,0,sizeof(vis)),ans+=hungary(i);
我们回过头看这两个性质,性质1是算法的基础,性质2确保了每个点只搜一次。
对证明感兴趣的可以看队爷论文,写的可好了,适合初学者看。
可以再看看 hall定理 虽然本文没有用到
好像还能用bfs写,据说常数更小。
以上。