您的位置:首页 > 其它

KM算法小结

2017-03-31 22:26 99 查看
正规的算法解释真看不懂。。。。

果然二分图这东西还是得和泡妞有关才好理解。。。 _ (:зゝ∠) _

推荐http://www.cnblogs.com/wenruo/p/5264235.html

正题

KM算法是用于解决带权二分图的完美匹配最大匹配值。(你不懂完美匹配,恭喜恭喜),是基于匈牙利算法的。

我惊讶的发现,空说点实在是不好理解(我智商低←_←),所以,下面把问题改成最令人感兴趣的泡妞问题。。。。

一个点集为男生,一个点集为女生,每一个女生对每一个男生有一个好感度(即边权),问如何配对使得最终好感度的和最大。

ex[i][j]表示第i个妹子对第j个男生的好感度。



思路

首先这东西为完美匹配,我们先把女生对男生没有好感度的(真可怜)视为好感度为0,那么匹配后自然就是一个完美匹配了!

接下来的问题就是最大匹配值。

每个妹子都期望有个好归宿,所以她们期望能够和自己好感度最高的男生牵手。

对此,我们引入数组exg[i]表示第i个妹子的期望,初始时exg[i]=max(ex[i][j])(即妹子对所有男生中,好感度最高的男生)。

由于现在社会状况为男女比例严重失调,所以男生已经不在乎妹子颜值了,只希望能有一个妹子就好,所以exb[i]表示第i个男生的期望,初始为exb[i]=0;



匹配前规定

开始匹配时,每一轮匹配中,每一个男生与女生只能尝试匹配一次!要求为ex[i][j]=exg[i]+exb[j](即双方都要满意对方)

匹配中

第一轮中,女1与男3匹配,成功。

女2与男3匹配,然而男3已经匹配过了,不能匹配,其他男生又不符合要求,这一轮匹配宣告失败。

匹配后

匹配失败后,参与匹配的有女1,女2,男3。

女生中有争执,只好降低自己的期望,女1要想可以匹配其他男生,至少要降低1点期望,女2也同理,所以她们的期望-1,即exg[1]-1,exg[2]-1.

而男3由于得到了妹子的关注,终生大事已经要着落了,自然就开始关注其妹子的颜值了,他的期望就上升1点,即exb[3]+1。

可以发现

1.只有参与匹配的男女才会进行调整。

2.男生上升的期望与女生下降的期望的值是相同的。



然后重复匹配,失败再进行调整,直到成功。

时间复杂度为O(n4)。

优化

设男女生调整的期望值为d

如果朴素的寻找最小d,是要n2级别的。

对此,我们可以设一个差距数组les[i],表示第i个男生最少要降低多少期望才能达到和一个妹子匹配的要求。一开始les[i]=∞

当我们匹配一个妹子和男生时,val=exg[i]+exb[j]-ex[i][j],当val=0时说明可以匹配,若val不为0(即val>0),说明男生还差val点期望才能获得这个女生的关注,所以les[j]=min(val,les[j])。

而d就是les中最小的。此时对于没有匹配到的男生,他们离妹子的期待又进了一步,这些男生的les[j]+=d。

时间为O(n)

最终整个算法的复杂度为O(n3)

题目

hdu2255

题目即为上

代码

#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)

using namespace std;

const int maxn=305;
int ex[maxn][maxn],exb[maxn],exg[maxn],les[maxn],n,ans,mat[maxn];
bool b[maxn],g[maxn];

bool dfs(int girl)
{
g[girl]=true;
fo(boy,1,n){
if (b[boy]) continue;
int val=exg[girl]+exb[boy]-ex[girl][boy];
if (!val){
b[boy]=true;
if (mat[boy]==-1||dfs(mat[boy])){
mat[boy]=girl;
return true;
}
}
else les[boy]=min(les[boy],val);
}
return false;
}
void KM()
{
fo(i,1,n) exb[i]=0,mat[i]=-1;
fo(i,1,n){
exg[i]=ex[i][1];
fo(j,2,n) exg[i]=max(exg[i],ex[i][j]);
}
fo(i,1,n){
memset(les,127,sizeof(les));
while (1){
fo(j,1,n) b[j]=g[j]=false;
if (dfs(i)) break;
int d=1e9;
fo(j,1,n)
if (!b[j]) d=min(d,les[j]);
fo(j,1,n){
if (g[j]) exg[j]-=d;
if (b[j]) exb[j]+=d;
else les[j]-=d;
}
}
}
fo(i,1,n) ans+=ex[mat[i]][i];
}
int main()
{
//freopen("T.in","r",stdin);
while (scanf("%d",&n)!=EOF){
fo(i,1,n)
fo(j,1,n) scanf("%d",&ex[i][j]);
ans=0;
KM();
printf("%d\n",ans);
}
}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  KM