前言
具体什么是二分图,如何判定,可以参考我的这篇博客。
定义
简单来说,就是二分图中有满足任意两条边没有相同的点的边的集合,称为一组匹配,而边数最多的一组匹配称为该二分图的最大匹配。在一组匹配中,属于这组边的称为匹配边,不属于的称为非匹配边,属于这组匹配的点称为匹配点,不属于的称为非匹配点。
匈牙利算法
又称增广路算法。
对于一组匹配 (M) ,若存在一条路径连接两个非匹配点,且使得匹配边与非匹配边交替出现,则称这条路径为增广路。
如上图,已匹配的边为红色,未匹配的边为绿色,则可以找到一组增广路 (8) ~ (2) ~ (7) ~ (4) 。不难发现增广路具有以下特点:
- 以非匹配边开始,在以非匹配边结尾,那么长度必为奇数。
- 路径上第一条边因为第一个点为奇数,则该路径上的第一个点为非匹配边,按照匹配边与非匹配边交替出现的性质可以得出,该路径的第奇数条边必为非匹配边,已经匹配的边必为第偶数条边,则有非匹配边的边数比匹配边的边数多一。
- 最大匹配中不会有增广路。证明:假设最大匹配中存在增广路,则可以将增广路中的所有边的状态取反(即把非匹配边转换为匹配边,将匹配边转换为非匹配边),得到另一组匹配,而这组匹配的匹配边肯定会比之前的一组匹配的边数多一,则之前的这组匹配就不是最大匹配,与假设矛盾,证毕。
在一张二分图中,若最大匹配为 (S) ,当且仅当 (S) 中不存在增广路。其确性基于 (hall) 定理,比较复杂就不在详讲,主要讲找二分图最大匹配的方法。
大体思想就是枚举左部点,找到增广路后将这条路上的所有边的状态取反,得到边数更大的一组匹配。
在匹配过程中,有两种情况会改变当前左部点 (u) 的匹配情况。
- 与之对应的右部点 (v) 是非匹配点,则可以将其的连边变为匹配边。
- 与之对应的右部点 (v) 是匹配点,但与 (v) 已经匹配的点 (u') 可以找到另一个未匹配的右部点 (v') ,则 (u) ~ (v) ~ (u') ~ (v') 为一条增广路,则将其状态取反。 (u) 对应的点 (v) 已经对 (u') 进行了标记,遍历 (u') 的时候就不会再遍历 (v) 了,看再次匹配 (u') 是否能找到上述的 (v') ,若找到 (v') ,意味着找到了增广路。
最小点覆盖于最大匹配的关系
由 (Konig) 定理可知:最小点覆盖的点数与最大匹配的边数相等。
证明较为复杂不详讲。
C++实现
int twin[MAXN];//右部节点与之匹配的左部节点
bool vis[MAXN];//记录当前点是否走过
bool Hungary(int now) {
int SIZ = v[now].size();
for(int i = 0; i < SIZ; i++) {
int next = v[now][i];
if(!vis[next]) {
vis[next] = true;
if(!twin[next] || Hungary(twin[next])) {//分别对应情况一与情况二
twin[next] = now;
return true;//匹配成功
}
}
}
return false;//匹配失败
}
int Match() {
int res = 0;
for(int i = 1; i <= n; i++) {
memset(vis, 0, sizeof(vis));
if(Hungary(i))
res++;
}
return res;
}