匈牙利算法
匈牙利算法是一个求解最大匹配问题的经典算法,被广泛应用于图论和网络流领域。该算法由匈牙利数学家Dénes Kőnig在20世纪30年代提出,用于解决两个集合之间的二分图最大匹配问题。
一、问题描述
在一个二分图中,有两个集合X和Y,它们的元素分别为{x1,x2,…,xm}和{y1,y2,…,yn},图中的边连接了X和Y中的元素。一个匹配M是指图中的一些边,它们连接了X和Y中的一些元素,且任意两条边没有公共顶点。最大匹配问题就是在这个二分图中找到一种匹配,使得匹配的边数最多。
二、算法思路与实现
匈牙利算法是一种基于增广路思想的贪心算法,其基本思路是在当前匹配M中寻找增广路,将其上的未匹配边和已匹配边交替翻转,从而增加匹配数。
具体实现上,我们维护两个数组:match数组和vis数组。match[i]表示与x[i]匹配的y点编号,如果x[i]没有匹配,那么match[i]=-1;vis[i]表示第i个x点是否被访问过。
然后,我们枚举每一个x点,如果x[i]还没有匹配,就尝试从它开始找增广路。对于每一个未匹配的x点i,我们在与它相连的每一个未匹配的y点j中尝试匹配,如果找到了增广路,就将增广路上的所有边的匹配状态进行翻转,然后继续查找下一个增广路,直到没有增广路为止。
为了查找增广路,我们使用了深度优先搜索(DFS)算法。从某个未匹配的x点i出发,我们将i标记为已访问,然后遍历与i相连的每一个未匹配的y点j。如果j没有被访问过,就继续从j出发进行DFS,如果j已经被访问过,并且可以通过其他路径到达另一个未匹配的x点k,那么就说明找到了一条增广路,将其上的边的匹配状态进行翻转。
from typing import List
MAXN = 1005 # 最大点数
match = [-1] * MAXN # 存储每个x点匹配的y点编号
vis = [False] * MAXN # 存储每个x点是否被访问过
def dfs(u: int, G: List[List[int]]) -> bool:
for v in G[u]:
if not vis[v]:
vis[v] = True
if match[v] == -1 or dfs(match[v], G):
match[v] = u
return True
return False
def hungarian(n: int, m: int, G: List[List[int]]) -> int:
res = 0
for i in range(1, n+1):
vis[1:] = [False] * m # 每次查找增广路前需要将vis数组重置
if dfs(i, G):
res += 1
return res
上面的代码中,我们定义了一个dfs函数和一个hungarian函数。其中dfs函数用于查找从未匹配的x点u开始的增广路,而hungarian函数用于执行整个匈牙利算法。
我们可以通过调用hungarian函数来计算二分图的最大匹配数。例如,假设我们有一个二分图,其中左部点集的元素个数为3,右部点集的元素个数为4,邻接矩阵为:
G = [
[1, 2, 3],
[2, 3],
[3, 4],
[1, 2, 3],
]
则可以通过如下代码调用匈牙利算法:
n = 3 # 左部点集的元素个数
m = 4 # 右部点集的元素个数
res = hungarian(n, m, G)
print(res) # 输出2,即二分图的最大匹配数
这里我们得到的结果是2,即该二分图的最大匹配数为2。
三、算法分析
匈牙利算法的时间复杂度为O(mn),其中m和n分别是二分图中两个集合的元素个数。这是由于算法的主要操作是在未匹配的x点中寻找增广路,每次寻找的时间复杂度为O(m),总共需要寻找n次。
由于匈牙利算法是一种基于增广路思想的贪心算法,它只能求解二分图的最大匹配问题,不能求解带权二分图匹配问题。如果需要求解带权二分图匹配问题,可以使用Kuhn-Munkres算法(也称为匈牙利算法的优化版,时间复杂度为O