二分图判定
使用黑白染色法,把与一个点相连的所有节点都染成与这个点不同的颜色,这样点就会被分为黑与白两个集合,如果说染色时出现了冲突,比如两个相连的点颜色均为黑色,那么黑点集中出现了边,此图便不是二分图
二分图最大匹配
任意两条边都没有公共端点的边集成为图的一组匹配,二分图中,对于包含边数最多的一组匹配称为二分图的最大匹配。匹配上的点称为匹配点,相应的有匹配边,非匹配边/点
增广路
对于一组匹配S,若二分图存在一条连接两个非匹配点的路径path,使得非匹配边与匹配边交替出现,则path就是匹配S的增广路,又称交错路
增广路具有的性质
- 长度length为奇数
- 路径上第1,3,5…length条边(第奇数条边)为非匹配边,第2,4,6…length-1条边为匹配边
因此我们如果把非匹配边变为匹配边,匹配边变为非匹配边,新的到的仍是一组匹配,并且匹配边数较原来的那组匹配增加了一条
进一步我们有推论,S为二分图的最大匹配,当且仅当图中不存在S的增广路(用反证法证明)
匈牙利算法(增广路算法)
求二分图最大匹配
算法过程是不断寻找增广路,每找到一次增广路,把增广路边取反,由增广路定义可知,匹配边数比原来的匹配多了一条
当一个点成为匹配点后,可能会因为寻找到了增广路,导致这个点更换匹配对象,但不可能使这个点变为非匹配点,因为增广路是匹配非匹配交错出现,取反后,对于路径两端点,他们成为匹配点,对于路径中的点,他们还是连接一条匹配边,一条非匹配边。
具体过程:首先认为图中不存在匹配边,即所有点都是非匹配点
设点X位于左部,点Y位于右部,X-Y有一条边
若Y是非匹配点,则X-Y为非匹配边,构成一条长度为1的增广路,对其取反,匹配边数加1
若Y已经和一个左部点匹配,则X-Y为非匹配边,找到Y的匹配点Z,再从Z出发去找另一个点k
- 若k是非匹配点,找到增广路
- 若k已经和一个左部点匹配,再找相应的匹配点,递归下去
不断重复这个过程,找到增广路后,回溯时通过改变匹配点进行取反,最后就得到二分图的最大匹配,为了避免重复搜索,还要在每次找增广路的时候开一个vis数组,搜过的点标记一下,下次访问便不再搜索,对于一次寻找增广路的过程,若从这个点出发找不到增广路,那么这个点无论一哪种方式被再次访问,依然找不到增广路
bool dfs(int x) {
for(int i=head[x]; i; i=e[i].nxt) {
int y = e[i].v;
if(vis[y]) continue;
vis[y] = 1;
if(!match[y] || dfs(match[y])) {
match[y] = x;//取反,匹配边是通过match来遍历的,所以通过邻接表遍历的是非匹配边
return true;
}
}
return false;
}
for(int i=1; i<=n; i++) {
memset(vis, 0, sizeof(vis));
if(dfs(i)) ans++;//n指左部所有节点
}