这是来自一个媒婆说亲的故事
相传在很久很久以前,还是在女权社会的时候,有一个很厉害的媒婆,只要她说的亲,就没有不成功的…
媒婆的生意是非常火爆的,这不,她现在手里就握着很多男孩子的信息,就准备去给各位女孩子挑了(毕竟女权社会嘛,男孩子的意见不重要)。
这天媒婆准备给四个女孩安排相亲,同时手里有四个男孩子的信息,这四个女孩子的名字分别是:汉库克,娜美,罗宾还有如花。四个男孩子分别是:路飞,索隆,香吉士还有园子。
几个女孩看了一下几个男孩子的信息后,几位女孩子心想:汉库克想我只要路飞。娜美想我可以是索隆还有香吉士,罗宾想我可以路飞,索隆和香吉士都可以,如花看了一下说,啊都好帅我全要!
这时候媒婆问汉库克:有喜欢的人吗?
汉库克:有,路飞
媒婆:行,路飞还没人要,给你了!
然后媒婆就去找娜美:你呢,喜欢谁?
娜美:索隆
媒婆:行,索隆还没人要,给你了!
然后媒婆去找罗宾了:姑娘可有喜欢的人啊?
罗宾:路飞啊!
媒婆心想:糟糕,这个已经有人要了
于是媒婆就跑去问汉库克:路飞罗宾也想要,你可不可以选 其他人。
汉库克:不行!老娘只要路飞!!!
媒婆只好去跟罗宾说:对不起,路飞已经有人要了,要不你选 另外的?
罗宾:那行,索隆也行。
媒婆心想:卧槽,索隆也有人要了啊
媒婆只好又去找娜美了:罗宾说她也想要索隆,你还有喜欢的 吗?
娜美:那香吉士?
媒婆:哈哈,行,这个没人要,给你了
媒婆很是高兴的去跟罗宾说:我给你谈妥了,索隆就给你了
来到如花问:你呢,喜欢谁?
如花:路飞。
媒婆心想:这肯定不行
但还是去问一下汉库克:你能否再考虑一下其他人?
汉库克:滚
媒婆:得嘞
媒婆又去找如花说:这个不行,选其他的
如花:那索隆
媒婆心想:这场景似曾相识啊??
又跑去问罗宾(因为之前已经把索隆许配给罗宾了嘛)
。。。
罗宾又重复了一下路飞、香吉士(索隆呢?媒婆让另选了),发现 并无法妥协(因为路飞汉库克不让啊,香吉士娜美不让,娜美不 让是索隆不能选了,路飞汉库克不让啊)
于是如花又得另选了。
如花说:香吉士也行
媒婆又跑去问娜美了(因为前面已经把香吉士改配给娜美了)
。。。
发现并无法妥协。
于是如花只能试一下最后的园子了
没想到媒婆说:可以,这个就给你了!
这就是整个算法流程了,于是最后媒婆说的姻缘就是:
汉库克–路飞
娜美–香吉士
罗宾–索隆
如花–园子
没什么比这更让人满意的了,媒婆几桩婚事都说成了。
但常常事情没有那么顺利,当给很多个女孩子说亲时,每个女 孩子都会有特定喜欢的人,有时候就很难调节了,媒婆就只能 牺牲小部分人,成全最多人。
那告诉你每个女孩子都喜欢谁,你能最多撮合几对呢?这就 是匈牙利算法的事情了
我们来回顾整个故事:
假设我们有一个函数,参数是女孩,函数的工作就是给这个女孩选夫婿。
一开始汉库克和娜美选的人都是名花无主,自然直接配对了。(没人要,直接匹配,自然就让多一个人满意了,所以是最好的选择)
但到了罗宾时,选了路飞时,发现路飞有人要了,罗宾这个后来的人非常强硬,我不管,我就要路飞,于是递归到让路飞之前配对过的人(即汉库克)重新选,但汉库克只有一个喜欢的人,没办法,满足罗宾的话,就不能满足汉库克,所以这并不能让多一个人满足(显然汉库克无法调节,但罗宾可能还有喜欢的人,所以维持现状是最好的选择)
然后汉库克又选了索隆,发现索隆已经有人要了,同样递归到索隆之前配对过的人(即娜美)重新选,现在函数的主体是娜美了,娜美除了索隆可以选其他喜欢的人(因为索隆相当于暂时许配给罗宾了),同样娜美又从第一个喜欢的人选起,即路飞(注意:这时候只知道索隆不能选)同样发现路飞有人了,所以同样迭代到汉库克(即前面路飞匹配过的人),所以现在函数的主体是汉库克,现在汉库克除了索隆和路飞,可以选择其他喜欢的人,没了这两个人后,汉库克无法选到其他喜欢的人,所以告诉上一层的人:我无法妥协,你找其他人吧。于是现在函数主体回到了娜美
娜美继续往下找喜欢的人,索隆不能选了,于是选到了香吉士,自然就将索隆匹配给罗宾了。(显然娜美在没有索隆后仍能满意,索隆给罗宾也能使罗宾满意,所以娜美的妥协是让多一个人满意,也是最好的选择)
可以发现这是一个递归的过程,遇到喜欢的人,能上就上,不能上就让已经匹配过的那个女孩妥协重新选,匹配过的女孩要是选到的男孩子也冲突,就继续往下妥协,直到找到能上的人,就递归回去说我可以妥协了,这个让给你,一直到第一层,这样就可以多一个人满足了。如图所示,深色黑线代表之前匹配过的
罗宾—索隆**------------------** 娜美-----香吉士
妥协后变成
罗宾**----------------------** 索隆-----娜美 **----------------------**香吉士
从只有一对满意变成两对满意,皆大欢喜。
代码解释:
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 X,一个集合 Y Y Y。
集合 X X X内元素不可以互相匹配, Y Y Y也一样。
告诉你集合 X X X到 Y Y Y的可能匹配,问最大匹配图,最终的匹配图只能是 X X X到 Y Y Y的一一对应。
比如 X X X集合是男孩, Y Y Y集合是女孩
可能的匹配是
b o y 1 − − g i r l 1 boy_1--girl_1 boy1−−girl1
b o y 1 − − g i r l 3 boy_1--girl_3 boy1−−girl3
b o y 2 − − g i r l 2 boy_2--girl_2 boy2−−girl2
b o y 2 − − g i r l 3 boy_2--girl_3 boy2−−girl3
b o y 3 − − g i r l 1 boy_3--girl_1 boy3−−girl1
则最大匹配图可以是(可能不唯一)
b o y 1 − − g i r l 3 boy_1--girl_3 boy1−−girl3
b o y 2 − − g i r l 2 boy_2--girl_2 boy2−−girl2
b o y 3 − − g i r l 1 boy_3--girl_1 boy3−−girl1
基本思想就是:当前女孩( g i r l x girl_x girlx)找到她心仪的男孩子,如果发现这个男孩子已经跟其他女孩子(如 g i r l k girl_k girlk)匹配了,就告诉 g i r l k girl_k girlk说你去找其他男孩子吧,这个男孩我要了,如果 g i r l k girl_k girlk能找到其他男孩子,那就皆大欢喜, g i r l k girl_k girlk就把 g i r l x girl_x girlx喜欢的男孩子让给 g i r l x girl_x girlx 了,可是如果 g i r l k girl_k girlk找不到其他男孩子,那凡事有个先来后到, g i r l x girl_x girlx就只能找下一个心仪的男孩子了。所以这是一个迭代的妥协过程。只有在能找到更大匹配图的时候才会妥协,因为没有哪个女孩子会拱手让人导致自己单身一个。
所以每次加入一个女孩,我们的目的是找到当前最优的匹配方案。即尽最大努力让更多的女孩满意
所以当加入完所有的女孩子,就能得到最大的匹配图了,即能知道能使最多多少个女孩子满意。
查找能不能找到增广路,即 g i r l x girl_x girlx加入竞争后能不能扩大当前匹配图代码
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
#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;
}
}