2 100 10 15 23
123
第一次接触KM算法,在学这个算法前最好先学二分匹配,算是二分图最大匹配的一个演变——带权匹配
网上讲KM的文章也不少,贴两个我学习看的帖子
http://philoscience.iteye.com/blog/1754498
http://blog.sina.com.cn/s/blog_691ce2b701016reh.html
我感觉第一个比较有助于理解,但代码实现是JAVA来写的,第二个可以学学具体写法,当然不是完全固定的,仅供参考。
这题就是个裸题,建个图跑遍KM就行。
代码如下:
#include <iostream>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <queue>
#include <list>
#include <algorithm>
#include <map>
#include <set>
#define LL long long
#define fread() freopen("in.in","r",stdin)
#define fwrite() freopen("out.out","w",stdout)
using namespace std;
const int INF = 0x3f3f3f3f;
const int msz = 10000;
const double eps = 1e-8;
//存图
int mp[333][333];
// x顶标 y顶标
int lx[333],ly[333],link[333],slack[333];
bool visx[333],visy[333];
int n;
bool cal(int x)
{
visx[x] = 1;
for(int y = 0; y < n; ++y)
{
if(visy[y]) continue;
int tmp = lx[x]+ly[y]-mp[x][y];
//边在二分匹配中
if(tmp == 0)
{
visy[y] = 1;
if(link[y] == -1 || cal(link[y]))
{
link[y] = x;
return 1;
}
}
//边不在二分匹配中
else slack[y] = min(slack[y],tmp);
}
return false;
}
int KM()
{
memset(ly,0,sizeof(ly));
memset(link,-1,sizeof(link));
for(int i = 0; i < n; ++i)
{
memset(slack,INF,sizeof(slack));
while(1)
{
memset(visx,0,sizeof(visx));
memset(visy,0,sizeof(visy));
//xi 得到匹配
if(cal(i)) break;
int d = INF;
for(int i = 0; i < n; ++i)
if(!visy[i]) d = min(d,slack[i]);
//匹配中的x顶标都减去d
for(int i = 0; i < n; ++i)
if(visx[i]) lx[i] -= d;
//匹配中的y顶标都加上d
//否则减少slack
for(int i = 0; i < n; ++i)
if(visy[i]) ly[i] += d;
else slack[i] -= d;
}
}
int ans = 0;
for(int i = 0; i < n; ++i)
if(link[i] != -1) ans += mp[link[i]][i];
return ans;
}
int main()
{
while(~scanf("%d",&n))
{
memset(lx,0,sizeof(lx));
for(int i = 0; i < n; ++i)
for(int j = 0; j < n; ++j)
{
scanf("%d",&mp[i][j]);
if(mp[i][j] > lx[i]) lx[i] = mp[i][j];
}
printf("%d\n",KM());
}
return 0;
}
原文:http://blog.csdn.net/challengerrumble/article/details/50572817