趣谈??匈牙利算法(二部图匹配算法,最大匹配图)

趣谈匈牙利算法

这是来自一个媒婆说亲的故事
相传在很久很久以前,还是在女权社会的时候,有一个很厉害的媒婆,只要她说的亲,就没有不成功的…

​ 媒婆的生意是非常火爆的,这不,她现在手里就握着很多男孩子的信息,就准备去给各位女孩子挑了(毕竟女权社会嘛,男孩子的意见不重要)。

​ 这天媒婆准备给四个女孩安排相亲,同时手里有四个男孩子的信息,这四个女孩子的名字分别是:汉库克,娜美,罗宾还有如花。四个男孩子分别是:路飞,索隆,香吉士还有园子。

​ 几个女孩看了一下几个男孩子的信息后,几位女孩子心想:汉库克想我只要路飞。娜美想我可以是索隆还有香吉士,罗宾想我可以路飞,索隆和香吉士都可以,如花看了一下说,啊都好帅我全要!

​ 这时候媒婆问汉库克:有喜欢的人吗?

​ 汉库克:有,路飞

​ 媒婆:行,路飞还没人要,给你了!

​ 然后媒婆就去找娜美:你呢,喜欢谁?

​ 娜美:索隆

​ 媒婆:行,索隆还没人要,给你了!

​ 然后媒婆去找罗宾了:姑娘可有喜欢的人啊?

​ 罗宾:路飞啊!

​ 媒婆心想:糟糕,这个已经有人要了

​ 于是媒婆就跑去问汉库克:路飞罗宾也想要,你可不可以选 其他人。

​ 汉库克:不行!老娘只要路飞!!!

​ 媒婆只好去跟罗宾说:对不起,路飞已经有人要了,要不你选 另外的?

​ 罗宾:那行,索隆也行。

​ 媒婆心想:卧槽,索隆也有人要了啊

​ 媒婆只好又去找娜美了:罗宾说她也想要索隆,你还有喜欢的 吗?

​ 娜美:那香吉士?

​ 媒婆:哈哈,行,这个没人要,给你了

​ 媒婆很是高兴的去跟罗宾说:我给你谈妥了,索隆就给你了

​ 来到如花问:你呢,喜欢谁?

​ 如花:路飞。

​ 媒婆心想:这肯定不行

​ 但还是去问一下汉库克:你能否再考虑一下其他人?

​ 汉库克:滚

​ 媒婆:得嘞

​ 媒婆又去找如花说:这个不行,选其他的

​ 如花:那索隆

​ 媒婆心想:这场景似曾相识啊??

​ 又跑去问罗宾(因为之前已经把索隆许配给罗宾了嘛)

​ 。。。

​ 罗宾又重复了一下路飞、香吉士(索隆呢?媒婆让另选了),发现 并无法妥协(因为路飞汉库克不让啊,香吉士娜美不让,娜美不 让是索隆不能选了,路飞汉库克不让啊)

​ 于是如花又得另选了。

​ 如花说:香吉士也行

​ 媒婆又跑去问娜美了(因为前面已经把香吉士改配给娜美了)

​ 。。。

​ 发现并无法妥协。

​ 于是如花只能试一下最后的园子了

​ 没想到媒婆说:可以,这个就给你了!

​ 这就是整个算法流程了,于是最后媒婆说的姻缘就是:

​ 汉库克–路飞

​ 娜美–香吉士

​ 罗宾–索隆

​ 如花–园子

​ 没什么比这更让人满意的了,媒婆几桩婚事都说成了。

​ 但常常事情没有那么顺利,当给很多个女孩子说亲时,每个女 孩子都会有特定喜欢的人,有时候就很难调节了,媒婆就只能 牺牲小部分人,成全最多人。

那告诉你每个女孩子都喜欢谁,你能最多撮合几对呢?这就 是匈牙利算法的事情了

我们来回顾整个故事:

​ 假设我们有一个函数,参数是女孩,函数的工作就是给这个女孩选夫婿。

​ 一开始汉库克和娜美选的人都是名花无主,自然直接配对了。(没人要,直接匹配,自然就让多一个人满意了,所以是最好的选择

​ 但到了罗宾时,选了路飞时,发现路飞有人要了,罗宾这个后来的人非常强硬,我不管,我就要路飞,于是递归到让路飞之前配对过的人(即汉库克)重新选,但汉库克只有一个喜欢的人,没办法,满足罗宾的话,就不能满足汉库克,所以这并不能让多一个人满足(显然汉库克无法调节,但罗宾可能还有喜欢的人,所以维持现状是最好的选择

​ 然后汉库克又选了索隆,发现索隆已经有人要了,同样递归到索隆之前配对过的人(即娜美)重新选,现在函数的主体是娜美了,娜美除了索隆可以选其他喜欢的人(因为索隆相当于暂时许配给罗宾了),同样娜美又从第一个喜欢的人选起,即路飞(注意:这时候只知道索隆不能选)同样发现路飞有人了,所以同样迭代到汉库克(即前面路飞匹配过的人),所以现在函数的主体是汉库克,现在汉库克除了索隆和路飞,可以选择其他喜欢的人,没了这两个人后,汉库克无法选到其他喜欢的人,所以告诉上一层的人:我无法妥协,你找其他人吧。于是现在函数主体回到了娜美

娜美继续往下找喜欢的人,索隆不能选了,于是选到了香吉士,自然就将索隆匹配给罗宾了。(显然娜美在没有索隆后仍能满意,索隆给罗宾也能使罗宾满意,所以娜美的妥协是让多一个人满意,也是最好的选择)

​ 可以发现这是一个递归的过程,遇到喜欢的人,能上就上,不能上就让已经匹配过的那个女孩妥协重新选,匹配过的女孩要是选到的男孩子也冲突,就继续往下妥协,直到找到能上的人,就递归回去说我可以妥协了,这个让给你,一直到第一层,这样就可以多一个人满足了。如图所示,深色黑线代表之前匹配过的

罗宾—索隆**------------------** 娜美-----香吉士

妥协后变成

罗宾**----------------------** 索隆-----娜美 **----------------------**香吉士

从只有一对满意变成两对满意,皆大欢喜。

代码解释:

bool finds(int x)
{
    for(int i=1;i<=n;i++)
    {
        if(!isp[i]&&girl[x][i])//girl[x][i]为一代表女孩x喜欢男孩i。isp[i]代表男孩i筛选过了,不能选或者选了后肯定冲突且无法妥协。
        {
            isp[i]=1;
            if(!boy[i]||finds(boy[i]))//能上就上,即男孩i等于零还没有人匹配,不能上就让男孩i匹配过的女孩boy[i]重新选,如果能妥协就是真,否则即是假,假的话女孩就得选下一个喜欢的人
            {
                boy[i]=x;
 //这代表已经为女孩x已经选到了男孩i,即使之前把男孩i配给其他女孩,但能到这一步,说明那个女孩已经匹配到另一个喜欢的人了
                return true;
            }
        }
    }
    return false ;
}

这个是主函数里面的代码:

for(int i=1;i<=m;i++)
  {
      memset(isp,false,sizeof isp);//注意:isp的作用更多体现在暂时不能选的人,如罗宾想要索隆,娜美就暂时不能要索隆一样,所以每次都要清零。
      if(finds(i))sum++;
  }

匈牙利算法

给定一个集合 X X ,一个集合 Y Y

集合 X X 内元素不可以互相匹配, Y Y 也一样。

告诉你集合 X X Y Y 的可能匹配,问最大匹配图,最终的匹配图只能是 X X Y Y 的一一对应。

比如 X X 集合是男孩, Y Y 集合是女孩

可能的匹配是

b o y 1 g i r l 1 boy_1--girl_1

b o y 1 g i r l 3 boy_1--girl_3

b o y 2 g i r l 2 boy_2--girl_2

b o y 2 g i r l 3 boy_2--girl_3

b o y 3 g i r l 1 boy_3--girl_1

则最大匹配图可以是(可能不唯一)

b o y 1 g i r l 3 boy_1--girl_3

b o y 2 g i r l 2 boy_2--girl_2

b o y 3 g i r l 1 boy_3--girl_1

基本思想就是:当前女孩( g i r l x girl_x )找到她心仪的男孩子,如果发现这个男孩子已经跟其他女孩子(如 g i r l k girl_k )匹配了,就告诉 g i r l k girl_k 说你去找其他男孩子吧,这个男孩我要了,如果 g i r l k girl_k 能找到其他男孩子,那就皆大欢喜, g i r l k girl_k 就把 g i r l x girl_x 喜欢的男孩子让给 g i r l x girl_x 了,可是如果 g i r l k girl_k 找不到其他男孩子,那凡事有个先来后到, g i r l x girl_x 就只能找下一个心仪的男孩子了。所以这是一个迭代的妥协过程。只有在能找到更大匹配图的时候才会妥协,因为没有哪个女孩子会拱手让人导致自己单身一个。

所以每次加入一个女孩,我们的目的是找到当前最优的匹配方案。即尽最大努力让更多的女孩满意

所以当加入完所有的女孩子,就能得到最大的匹配图了,即能知道能使最多多少个女孩子满意。

查找能不能找到增广路,即 g i r l x girl_x 加入竞争后能不能扩大当前匹配图代码

bool finds(int x)
{
    for(int i=1;i<=n;i++)
    {
        if(!isp[i]&&girl[x][i])//girl[x][i]表示girl_x能否与boy_i匹配,isp[i]=1表示通过改点无法找到增广路。
        {
            isp[i]=1;
            if(!boy[i]||finds(boy[i]))
            {
                boy[i]=x;
                return true;
            }
        }
    }
    return false ;
}
for(int i=1;i<=m;i++)
  {
      memset(isp,false,sizeof isp);
      if(finds(i))sum++;
  }

例题:
HDU2063
代码:

#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
const int MX=5e2+7;
const int p=9973;
const int M=12;
const int mod=1e9+7;
using namespace std;
ll qpow(ll a,ll b,ll MOD=mod){for(ll ans=1;;a=a*a%MOD,b>>=1){if(b&1)ans=ans*a%MOD;if(!b)return ans;}}
ll inv(ll a,ll MOD=mod){return qpow(a,MOD-2,MOD);}
ll __gcm(ll a,ll b){return a*b/__gcd(a,b);}
int boy[MX],girl[MX][MX];
bool isp[MX];
int n;
bool finds(int x)
{
    for(int i=1;i<=n;i++)
    {

        if(!isp[i]&&girl[x][i])
        {
            isp[i]=1;
            if(!boy[i]||finds(boy[i]))
            {
                boy[i]=x;
                return true;
            }
        }
    }
    return false ;
}
int main()
{
  ios::sync_with_stdio(0),cin.tie(0);
  int k,m;

  while(cin>>k){
    if(k==0)break;
  cin>>m>>n;
  rep(i,1,m)rep(j,1,n)girl[i][j]=0;
  rep(i,1,n)boy[i]=0;
  for(int i=1;i<=k;i++)
  {
      int a,b;
      cin>>a>>b;
      girl[a][b]=1;
  }
  int sum=0;

  for(int i=1;i<=m;i++)
  {
      memset(isp,false,sizeof isp);
      if(finds(i))sum++;
  }
  cout<<sum<<endl;
  }
}
发布了109 篇原创文章 · 获赞 35 · 访问量 5986

猜你喜欢

转载自blog.csdn.net/weixin_43965698/article/details/104391288