一般图最大匹配——带花树

所谓花,就是如下图所示的一个奇环:
1

本文中粗边代表现在的匹配边,细边代表该点的前驱(后文会讲解前驱是什么,现在只需要知道每个点和它的前驱在原图中一定是有边的)。

如图所示,一朵包含\(2k+1\)个点的花一定至多包含\(k\)条匹配边,于是总会剩下一个未匹配的点,上图中即为\(1\)号点。

那么我们可以发现,如果有另外一个点想要与花中的某个点\(v\)匹配,那么有两种情况:1、\(v\)是未匹配的点(即1号点),那么直接与\(v\)匹配即可。2、\(v\)是已经匹配的点,这时只要将花中的匹配状况修改,使得\(v\)变成未匹配的那个点即可。

综上所述,只要花中的点没有向外匹配,我们总是可以使得外部的一个点和花中任意一个点匹配,因此花的性质和点其实很相似。我们将花缩成一个点来处理,就可以解决出现奇环的问题。以上思想就是带花树算法的核心。

==================总之分割一下好了==================

带花树算法的过程其实和\(bfs\)版本的匈牙利是很相似的,都是找出一个交错树,交错树可能长这样(注意每个蓝色点可能有多个橙色儿子,但是每个橙色点只能有一个蓝色儿子):
2

其中1号点就是我们尝试增广的节点,在这里我们给每一个节点一个\(type\)值,若该点不在交错树中,它的\(type\)值为\(0\),否则为\(1\)\(2\)。上图中我们用蓝色点代表\(type=1\)的点,橙色点代表\(type=2\)的点,不难看出\(type\)值的不同其实代表了一种类似于二分图的关系,每个点在交错树中只和\(type\)值不同的点相连。当我们没有找到奇环的时候,\(type\)值和二分图是等价的。

那么仿照匈牙利的过程,我们将尝试增广的点\(v\)\(type\)值设为\(1\)并开始增广,假设当前处理的点为\(u\)

1、如果\(type_u=0\),就代表它不在交错树中:

\(u\)已经有匹配时,我们就扩展这棵交错树,将\(type_u\)的值设为\(2\)(因为其和\(type\)值为\(1\)\(v\)相邻),并将\(type_{match_u}\)的值设为\(1\)(同理)。这时我们就可以把\(match_u\)塞进队列里了,如果能够沿着\(match_u\)找到增广路的话我们就可以让\(match_u\)匹配那个增广的点并将\(u\)\(v\)匹配,这样就使匹配数增加了\(1\)。同时我们将\(u\)的前驱(用\(pre_u\)表示)设置为\(v\),这是为了方便在找到增广路以后一路返回修改匹配。

\(u\)并没有匹配时,我们就成功找到了一条增广路,此时沿着由\(pre\)\(match\)连成的边一路修改就增广完成了,返回。

2、如果\(type_u=2\),代表你找到了一个偶环,并没有什么用,就跳过这个点。

3、这里是最重点的,如果\(type_u=1\),代表你找到了一个奇环,这就代表你的\(type\)值不再等价于二分图了,我们这个时候就可以开始“缩花”操作,将我们找到的奇环缩成一个点。让我们具体的考虑一下:

首先,快速找到哪些点在这个奇环中,显然\(u\)\(v\)一定都出现在交错树上(\(type\)不为\(0\)),结合上面的那张图考虑,奇环的范围就是两个点在交错树上的链中包含的所有点,因此我们需要找到这两个点的\(lca\),这里直接采用暴力向上跳的做法即可。

找到以后怎么连接\(pre\)边呢?我们参考一下文章开头的结构,可以发现此时的\(lca\)一定就是那个花中唯一的没有匹配或者匹配到外面节点的\(1\)号点。因此感性思考一下,我们应该“诱导”其他所有的点通过\(pre\)\(match\)边一路走走到\(lca\)上,因此将除了与\(lca\)相连的\(pre\)边外其他的\(pre\)边都改为双向边,并将\(u\)\(v\)也改成双向边即可达到这个目的。

最后做一点扫尾工作,我们可以通过并查集将奇环缩成一个点(因此在普通增广的时候也要考虑并查集的情况),不妨用\(lca\)做这朵花的代表。同时再考虑一下这个新点的\(type\)值应该设为什么,因为每个橙色点最多只有一个儿子(它的匹配点),因此\(lca\)一定是蓝色点,因此新点的\(type\)值为\(1\),所以要注意将花中所有\(type\)值为\(2\)的点修改为\(1\)并且加入队列。

当我们找不到新的点时,本次增广失败。

===============分割分割================

以上就是一般图最大匹配的增广过程,注意在每次增广之前,\(pre\)\(type\)以及并查集都是要初始化的。将并查集的复杂度看作常数,则每次增广至多是\(O(m)\)的,一共需要增广\(O(n)\)次,因此带花树算法的复杂度和匈牙利一样,都是\(O(nm)\),当然,在实践中带花树算法跑得一般会比理论上界快很多。

相信在熟练理解带花树的过程后一定能写出代码,因此为了不对读者造成思维定式影响这里就不贴代码了。完结撒花~

猜你喜欢

转载自www.cnblogs.com/Mr-Spade/p/9636874.html