图论专题-学习笔记:匈牙利算法
1. 前言
本篇博文将会专门讲述匈牙利算法的具体思路,实现过程以及正确性证明。
匈牙利算法是在 内的时间内寻找二分图的最大匹配的一种算法,其中 为左部点个数, 为右部点个数。
在学习匈牙利算法之前,请先确保掌握以下名词:
- 二分图
- 匹配与最大匹配
- 增广路
如果对上述部分名词没有掌握,请先掌握后再来学习。
懒得百度?传送门:图论专题-学习笔记:二分图基础
2. 例题
由于自然语言描述匈牙利算法难懂且难表述,直接采用图示方法讲解。
(绘图工具:Windows 画图软件)
为了理解方便,我们假设左边的 表示 4 个人,这 4 个人去吃饭, 表示 4 道菜,每个人都有自己喜欢的菜,而连线就表示喜欢的菜。
没错 B2 因为太难吃了以至于没人喜欢
每个人至多只能选择一道菜。
先给 分菜。 喜欢吃 ,那么就将 分配给 。
那么分配如下所示(红色边就是匹配):
再给 分菜。 也喜欢吃 ,于是就有了这样一段对话:
:“我说 呀,我也想吃 ,您就不能换一道菜吗?”
:“啊这?好吧, 给你,我吃 。”
于是当前分配如下:
现在考虑 。糟糕, 也想吃 。
:“ 在吗?您可不可以换一道菜啊,我也想吃 。”
:“啊这?但是我只喜欢这一道菜,根据先到先得原则,我不能让给您。”
于是 没有吃到 ,但是 还喜欢 啊!于是 选择了 这道菜。
那么当前分配如下:
最后是 。 也想吃 ,而且也只喜欢 。很遗憾的是, 已经被 选走了,且 不愿意换。
最后, 谁都没选到。
因此结果为 3。
你会发现,实际上上面的所有过程就是寻找最大匹配的过程。
简单总结一下:
- 如果新来的人想选择一道菜且这道菜没有被选,那么就选上。
- 如果想选的菜冲突了,以前的换一道菜。
- 但是如果以前的菜不能换,那么新来的人只能换一道菜。
- 如果新来的人想选的菜都选不了,那么就别吃了。
有关该算法的系统语言描述以及正确性证明见代码后面。
代码(推荐先将后面理论看完再看代码):
/*
========= Plozia =========
Author:Plozia
Problem:P3386 【模板】二分图最大匹配
Date:2021/3/14
========= Plozia =========
*/
#include <bits/stdc++.h>
using std::vector;
typedef long long LL;
const int MAXN = 500 + 10;
int n, m, e, Match[MAXN], ans;
vector <int> Next[MAXN];
bool book[MAXN];
int read()
{
int sum = 0, fh = 1; char ch = getchar();
for (; ch < '0' || ch > '9'; ch = getchar()) fh -= (ch == '-') << 1;
for (; ch >= '0' && ch <= '9'; ch = getchar()) sum = (sum << 3) + (sum << 1) + (ch ^ 48);
return (fh == 1) ? sum : -sum;
}
bool dfs(int k)
{
for (int i = 0; i < Next[k].size(); ++i)
{
int u = Next[k][i];
if (book[u]) continue;
book[u] = 1;
if (!Match[u] || dfs(Match[u])) {Match[u] = k; return 1;}//寻找增广路
}
return 0;
}
void Hungary()
{
memset(Match, 0, sizeof(Match));
for (int i = 1; i <= n; ++i)
{
memset(book, 0, sizeof(book));//注意重置
if (dfs(i)) ++ans;
}
}
int main()
{
n = read(), m = read(), e = read();
for (int i = 1; i <= e; ++i)
{
int u = read(), v = read();
Next[u].push_back(v);
}
Hungary();
printf("%d\n", ans);
return 0;
}
那么上面算法的本质是什么呢?
考虑一下 和 的对话。
我们发现,在 还没有匹配之前, 为 非匹配点。
为一条 非匹配边。
而 为一条 匹配边。
为一条 非匹配边, 为 非匹配点。
于是:
路径 为一条 增广路!
增广路!
在二分图#1(link)中作者提到过增广路有一条重要性质:
- 当增广路上非匹配边比匹配边数量大一,那么将非匹配边改为匹配边,匹配边改为非匹配边,那么该路径依然是增广路而且匹配数加一。
于是我们再结合上面的图示来理解,正确性就显然了。
根据这个性质,在保证路径不变的情况下我们能够尽可能的增加匹配数,而最大匹配一定在若干条增广路上,且增广路上匹配数达到最大。
于是正确性证完了。
因此,匈牙利算法的本质就是不断寻找增广路来扩大匹配。
而代码中的 数组就是表示匹配, 表示 是一条匹配边。
3. 总结
匈牙利算法的本质就是不断寻找增广路来扩大匹配。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具