匈牙利算法
匈牙利算法是一种在多项式时间内求解任务分配问题的组合优化算法,并推动了后来的原始对偶方法。——————百度百科
咳咳,匈牙利算法是一种常用于求二分图最大匹配的高效算法,将暴力的指数级算法进化到多项式复杂度,如果用邻接表存图,最坏时间复杂度为
二分图又称作二部图,是图论中的一种特殊模型。 设
二分图最大匹配是指在二分图中,所有匹配中包含最多匹配边的匹配。在二分图中,顶点被分为两个互不相交的子集,图中的每条边连接这两个子集的顶点。最大匹配是指在一个图中所有可能的匹配中,匹配边数最多的匹配。其中,一个匹配中所有边互不相交,即没有公共点。
二分图最小点覆盖是指选出最少数量的点,构成点集
二分图最大独立集是指在二分图的点全集
这里给出一些变换公式:
以下设二分图的点全集为
最小点覆盖=最大匹配。
证明:首先,假设已经给定了最大匹配,那么最小点覆盖数量显然大于等于最大匹配数。然后反过来,假定给出了最小点覆盖,那么称点覆盖集为V,根据定义,每个集合内的点肯定都能找到至少一条边,使得边都不相交,否则这个点的覆盖是没有必要的,与“最小点覆盖”矛盾,所以最大匹配大于等于最小点覆盖,综上,最大匹配等于最小点覆盖。
最大独立集=
证明:假设给定最小点覆盖集,那么得知集合外的点都是独立的,即独立集大小>=总点数-覆盖集大小。假设给定最大匹配,那么有独立集大小<=总点数-最大匹配,又因为最大匹配等于最小点覆盖,所以独立集大小=总点数-覆盖集大小。
最小边覆盖=最大点独立集
证明:结论显然
DAG的最小路径覆盖=总点数-拆点后的最大匹配
证明:令左部点是入点,右部点是出点,那么路径数可以转化为左部点没有匹配的点的数量,因为这些点都可以视为路径起点,那么我们为了让起点尽可能少,显然要让匹配尽可能多,由此得出如上结论,这个结论还是挺巧妙的。
匈牙利算法的主要流程是这样:从左部点的每个点枚举出发,不断寻找增广路。
这是基于深度优先的匈牙利算法。
下面附上模板题
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 510;
int n, m, E;
vector <int> e[N];//动态数组存图
int vis[N], match[N];//vis表示这个右部点是否被访问过,match表示这个右部点的匹配
int path(int u)
{
for (int v : e[u])
{
if (!vis[v])//记录vis是为了防止后续重复访问,影响正确性
{
vis[v] = 1;
if (!match[v] || path(match[v]))//如果右部点还没有匹配,或者其匹配可以匹配别的匹配()
{
match[v] = u;//更新匹配
return 1;
}
}
}
return 0;
}
void mian()
{
cin >> n >> m >> E;
int u, v;
for (int i = 1; i <= E; i ++)
{
cin >> u >> v;
e[u].push_back(v);
}
int ans = 0;
for (int i = 1; i <= n; i ++)
{
if (path(i)) ans ++;
memset(vis, 0, sizeof(vis));//每次枚举完都要清空
}
cout << ans << endl;
}
int main()
{
int T = 1;
while (T --) mian();
return 0;
}
这里还有一个小技巧,就是当遇上棋盘这一类问题时,如果时间复杂度正确,但是超时了,那么可以试着考虑改变图的遍历顺序。比如:如果你是从左上到右下枚举匹配,那么可以将遍历顺序写成从右下往左上,这样能够提升一定效率。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效