二分图最大匹配算法
- https://www.renfei.org/blog/bipartite-matching.html
- https://zoj.pintia.cn/problem-sets/91827364500/problems/91827364639
我觉得链接文章写得非常好,看这一篇就足够了。匈牙利算法的本质思想,还是不断地寻找增广路径。 对于 网络最大流问题 来说,增广路径就是在残留图上寻找s->t的可行路径,而对于二分图来说,增广路径是 "一条s,t分别是未匹配节点,中间所经过的节点都是匹配节点的路径"。
以下图为例,9->4->8->1->6->2就是一条增广路径
- (4,8),(1,6)是匹配节点
- 利用增广路径的话,我们可以增加一对匹配节点(多一条匹配边)
- 修改后匹配节点为 (9,4), (8,1), (6,2).
在算法最外层,我们需要遍历所有“未匹配”的节点,所以外层循环时间复杂度是O(V). 寻找增广路径的时间复杂度则是O(E). 所以总体时间复杂度是O(VE). 我的算法(BFS版本)在实现上和链接文章的里面给的实现有点不同,一个差别是将处理增广路径单独分离出来, 另外一个差别就是增广路径的表示我觉得会更加清晰一些。另外我的实现中顶点编号从1开始。
def hungarian(adj): n = len(adj) matching = [0] * n check = [0] * n parent = [0] * n from collections import deque dq = deque() for t in range(1, n): if matching[t] != 0: continue dq.clear() dq.append(t) check[t] = t parent[t] = 0 ending = 0 while dq and ending == 0: u = dq.popleft() for v in adj[u]: if check[v] == t: continue check[v] = t parent[v] = u if matching[v] == 0: ending = v break else: parent[matching[v]] = v dq.append(matching[v]) # found a augmented path. # ending -> p[ending] -> .. x -> t -> 0 path = [] while ending != 0: p = parent[ending] path.append(ending) path.append(p) matching[ending] = p matching[p] = ending ending = parent[p] path = path[::-1] print('augpath', path) return matching