初识二分图最大匹配(匈牙利算法)

匈牙利算法就像是一个和事佬,从中调解尽量满足每个人的需求。


        由于《啊哈!算法》中二分图的最大匹配是最后一章的最后一个内容加上篇幅太小,我也没有扩展多少,所以本博客内容不多,如果你也学了《啊哈!算法》,那本篇博客可以看作是我们的交流。        

例题:我要做月老

        小哼今天和小伙伴一起去游乐场游玩,过山车的每一排只有两个位置,为了安全起见,每个女生都必须与一个男生坐一排,但是每个人都希望与自己认识的人坐在一起。举个例子吧。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,所以时间复杂度为O(MN)

HK算法

        相比较匈牙利算法(KM算法),HK算法的精确度更高。实际上HK算法是在KM算法上的优化,KM算法在每次迭代中,只是找到一条增广路,然后基于该增广路,对当前解进行改进。但是每次迭代中,却往往存在多条增广路,如果能找到多条增广路,算法效果会得到进一步提升。而HK算法正式利用了这一点,在每一次算法迭代中,同时使用了DFS和BFS,因此可以在单次迭代中找到多条增广路,从而进一步提升了效率,缩小了算法复杂度

       这里由于我自己还不会,不再详细介绍。 

参考文献

《啊哈!算法》

(通俗易懂小白入门)二分图最大匹配——匈牙利算法 - 白泽talk - 博客园

优化 | 二部图最大匹配问题的精确算法详解(HK算法和匈牙利算法):一份让您满意的【理论介绍+代码实现】学习笔记 - 知乎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值