匈牙利匹配算法分享

匈牙利算法

匈牙利算法是一个求解最大匹配问题的经典算法,被广泛应用于图论和网络流领域。该算法由匈牙利数学家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

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值