[算法] 二分图最大匹配

前言

具体什么是二分图,如何判定,可以参考我的这篇博客

定义

简单来说,就是二分图中有满足任意两条边没有相同的点的边的集合,称为一组匹配,而边数最多的一组匹配称为该二分图的最大匹配。在一组匹配中,属于这组边的称为匹配边,不属于的称为非匹配边,属于这组匹配的点称为匹配点,不属于的称为非匹配点。

匈牙利算法

又称增广路算法。

对于一组匹配 \(M\) ,若存在一条路径连接两个非匹配点,且使得匹配边与非匹配边交替出现,则称这条路径为增广路。

在这里插入图片描述
如上图,已匹配的边为红色,未匹配的边为绿色,则可以找到一组增广路 \(8\) ~ \(2\) ~ \(7\) ~ \(4\) 。不难发现增广路具有以下特点:

  • 以非匹配边开始,在以非匹配边结尾,那么长度必为奇数。
  • 路径上第一条边因为第一个点为奇数,则该路径上的第一个点为非匹配边,按照匹配边与非匹配边交替出现的性质可以得出,该路径的第奇数条边必为非匹配边,已经匹配的边必为第偶数条边,则有非匹配边的边数比匹配边的边数多一。
  • 最大匹配中不会有增广路。证明:假设最大匹配中存在增广路,则可以将增广路中的所有边的状态取反(即把非匹配边转换为匹配边,将匹配边转换为非匹配边),得到另一组匹配,而这组匹配的匹配边肯定会比之前的一组匹配的边数多一,则之前的这组匹配就不是最大匹配,与假设矛盾,证毕。

在一张二分图中,若最大匹配为 \(S\) ,当且仅当 \(S\) 中不存在增广路。其确性基于 \(hall\) 定理,比较复杂就不在详讲,主要讲找二分图最大匹配的方法。

大体思想就是枚举左部点,找到增广路后将这条路上的所有边的状态取反,得到边数更大的一组匹配。

在匹配过程中,有两种情况会改变当前左部点 \(u\) 的匹配情况。

  1. 与之对应的右部点 \(v\) 是非匹配点,则可以将其的连边变为匹配边。
  2. 与之对应的右部点 \(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;
}

看看自己做对没有吖

posted @ 2020-12-01 20:11  Last_Breath  阅读(492)  评论(3编辑  收藏  举报