匈牙利算法就像是一个和事佬,从中调解尽量满足每个人的需求。
由于《啊哈!算法》中二分图的最大匹配是最后一章的最后一个内容加上篇幅太小,我也没有扩展多少,所以本博客内容不多,如果你也学了《啊哈!算法》,那本篇博客可以看作是我们的交流。
例题:我要做月老
小哼今天和小伙伴一起去游乐场游玩,过山车的每一排只有两个位置,为了安全起见,每个女生都必须与一个男生坐一排,但是每个人都希望与自己认识的人坐在一起。举个例子吧。1号女生与1号男生相互认识,因此1号男生可以和1号女生坐在一起。另外1号女生与2号男生相互认识,因此他们也可以坐在一起。像这样的关系还有2号女生认识2号和3号男生,3号女生认识1号男生。请问如何安排座位才能让更多的人满意呢?这仅仅是一个例子。实际情况要复杂得多,因为小哼的小伙伴实在太多了。
输入格式:
输入m+1行,第一行两个数n和m,n表示男生和女生的人数均为n,m表示m个关系。接下来的m行,每行形如“u v”,用来表示u号男生可以和v号女生坐在一起。
输出格式:
输出一行,一个数表示最大匹配对数。
输入样例:
3 5
1 1
1 2
2 2
2 3
3 1
输出样例:
3
这道题便是很经典的二分图匹配问题,男生为一个集合,女生为一个集合,二者连线,并且不能有公共的顶点。
二分图
二分图中顶点可以被分为两个互不相交的集合,且图中的每条边连接的两个顶点分别属于这两个不同的集合,如男生和女生集合。
匈牙利算法
首先想到的是遍历,比如1号男生和1号女生匹配,2号男生和2号女生(如左图所示),但是这样肯定不是最大匹配,因为3号男生与3号女生不认识。
我们知道u号男生可能不止与一个女生认识,所以我们利用这一点。当三号男生寻找时,发现他认识1号女生,但是1号女生已经和一号男生相匹配,所以我们可以让一号男生换一个女生匹配,如果可以匹配成功,那就又增加了一条匹配路线。
发现1号男生还认识2号女生,但是2号女生和2号男生相匹配,所以再问问3号男生......依次递归,最后就能形成如上边有图的结果。可以理解为是一直在寻找增广路(专业名词)。
思路就是这样,相比较代码没那么容易理解,这里我们用match[u]=v,来表示u号男生与v号女生相匹配。通过深度优先搜索来遍历(广度也可以)。
核心代码:
int dfs(int u)
{
for (int i = 1; i <= n; i++)/*女生*/
{
if (book[i]==0 && e[u][i]==1)/*可以配对并且女生在本轮没有被标记(不一定没配对)*/
{
book[i]=1;
if (match[i]==0 || dfs(match[i]))/*女生没有配对或者女生配对的男生可以找到新的配对方案*/
{
match[i] = u;
return 1;
}
}
}
return 0;
}
完整代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int n,m;
int e[9][9]={0};
int book[9]={0};
int match[9]={0};
int dfs(int u)
{
for (int i = 1; i <= n; i++)/*女生*/
{
if (book[i]==0 && e[u][i]==1)/*可以配对并且女生在本轮没有被标记(不一定没配对)*/
{
book[i]=1;
if (match[i]==0 || dfs(match[i]))/*女生没有配对或者女生配对的男生可以找到新的配对方案*/
{
match[i] = u;
return 1;
}
}
}
return 0;
}
int main()
{
int sum;
scanf("%d%d",&n,&m);
for (int i = 0,t1,t2; i < m; i++)
{
scanf("%d%d",&t1,&t2);
e[t1][t2]=1;
}
for (int i = 1; i <= n; i++)/*男生*/
{
memset(book,0,sizeof(int)*9);
if (dfs(i))
{
sum++;
}
}
printf("sum=%d\n",sum);
return 0;
}
复杂度分析:
如果二分图有n个点,那么最多有n/2个增广路径。如果图中共有m条边,那么每找一条增广路径最多把所有边遍历一遍,所花的时间是m,所以时间复杂度为。
HK算法
相比较匈牙利算法(KM算法),HK算法的精确度更高。实际上HK算法是在KM算法上的优化,KM算法在每次迭代中,只是找到一条增广路,然后基于该增广路,对当前解进行改进。但是每次迭代中,却往往存在多条增广路,如果能找到多条增广路,算法效果会得到进一步提升。而HK算法正式利用了这一点,在每一次算法迭代中,同时使用了DFS和BFS,因此可以在单次迭代中找到多条增广路,从而进一步提升了效率,缩小了算法复杂度。
这里由于我自己还不会,不再详细介绍。
参考文献
《啊哈!算法》
(通俗易懂小白入门)二分图最大匹配——匈牙利算法 - 白泽talk - 博客园
优化 | 二部图最大匹配问题的精确算法详解(HK算法和匈牙利算法):一份让您满意的【理论介绍+代码实现】学习笔记 - 知乎