您的位置:首页 > 其它

二分图匹配之最佳匹配——KM算法

2017-01-16 22:13 169 查看
今天也大致学了下KM算法,用于求二分图匹配的最佳匹配。

何为最佳?我们能用匈牙利算法对二分图进行最大匹配,但匹配的方式不唯一,如果我们假设每条边有权值,那么一定会存在一个最大权值的匹配情况,但对于KM算法的话这个情况有点特殊,这个匹配情况是要在完全匹配(就是各个点都能一一对应另一个点)情况下的前提。

自然,KM算法跟匈牙利算法有相似之处。

其算法步骤如下:

1.用邻接矩阵(或其他方法也行啦)来储存图,注意:如果只是想求最大权值匹配而不要求是完全匹配的话,请把各个不相连的边的权值设置为0

2.运用贪心算法初始化标杆。

3.运用匈牙利算法找到完备匹配。

4.如果找不到,则通过修改标杆,增加一些边。

5.重复3,4的步骤,直到完全匹配时可结束。

一言不合地冒出了个标杆??标杆是什么???

在解释这个问题之前,我们先来假设一个很简单的情况,用我们人类伟大的智能思维去思考思考。

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int qwq=0x7fffffff;
int w[1000][1000];  //w数组记录边权值
int line[1000],usex[1000],usey[1000],cx[1000],cy[1000];  //line数组记录右边端点所连的左端点, usex,usey数组记录是否曾访问过,也是判断是否在增广路上,cx,cy数组就是记录点的顶标
int n,ans,m;  //n左m右
bool find(int x){
usex[x]=1;
for (int i=1;i<=m;i++){
if ((usey[i]==0)&&(cx[x]+cy[i]==w[x][i])){   //如果这个点未访问过并且它是子图里面的边
usey[i]=1;
if ((line[i]==0)||find(line[i])){   //如果这个点未匹配或者匹配点能更改
line[i]=x;
return true;
}
}
}
return false;
}
int km(){
for (int i=1;i<=n;i++){  //分别对左边点依次匹配
while (true){
int d=qwq;
memset(usex,0,sizeof(usex));
memset(usey,0,sizeof(usey));
if (find(i)) break;  //直到成功匹配才换下一个点匹配
for (int j=1;j<=n;j++)
if (usex[j])
for (int k=1;k<=m;k++)
if (!usey[k]) d=min(d,cx[j]+cy[k]-w[j][k]);  //计算d值
if (d==qwq) return -1;
for (int j=1;j<=n;j++)
if (usex[j]) cx[j]-=d;
for (int j=1;j<=m;j++)
if (usey[j]) cy[j]+=d;     //添加新边
}
}
ans=0;
for (int i=1;i<=m;i++)
ans+=w[line[i]][i];
return ans;
}
int main(){
while (~scanf("%d%d",&n,&m)){
memset(cy,0,sizeof(cy));
memset(w,0,sizeof(w));
memset(cx,0,sizeof(cx));
for (int i=1;i<=n;i++){
int d=0;
for (int j=1;j<=n;j++){
scanf("%d",&w[i][j]);
d=max(d,w[i][j]);   //此处顺便初始化左边点的顶标
}
cx[i]=d;
}
memset(line,0,sizeof(line));
printf("%d\n",km());
}
return 0;
}


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