您的位置:首页 > 其它

poj 2195 KM算法(完备匹配)

2011-05-12 20:39 197 查看
题目链接:http://poj.org/problem?id=2195

题目大意:在n*m的矩阵中,有一些man和house,数目相等,要使每个man都回到一个house里,所走的距离为对应位置的行号差和列号差之和,求所需走的最小距离并输出

思路:很明显是二分图最小权值匹配问题,由于KM算法求的是最大权值匹配,故要将权值取负,然后用KM模板即可,最后结果再取反输出

对于KM算法,我还是处于懵懂状态,需要继续理解

貌似网上有用最小费用最大流解决的spfa方法,由于还没学,故有待继续学习

#include<stdio.h>
#include<math.h>
#include<string.h>
#define N 110
#define MAX 100000000
int visitx
,visity
,match
,w

,slack
,lx
,ly
;
char map

;
int hcount,mcount;
struct node{
int r,c;
}man
,home
;
int dfs(int p) /*寻找完备匹配*/
{
int i,t,temp;
visitx[p]=1;
for(i=1;i<=hcount;i++)
{
if(visity[i])
continue;
temp=lx[p]+ly[i]-w[p][i];
if(temp==0)
{
visity[i]=1;
t=match[i];
match[i]=p;
if(t==0 || dfs(t))
return 1;
match[i]=t;
}
else if(slack[i]>temp)
slack[i]=temp;  /*松弛,减小下面求d值时的时间复杂度*/
}
return 0;
}
int KM()
{
int i,j,d,ans;
memset(ly,0,sizeof(ly));
memset(match,0,sizeof(match));
for(i=1;i<=mcount;i++)
{
lx[i]=-MAX;  /*注意此处初始化,是负值*/
for(j=1;j<=hcount;j++)
if(lx[i]<w[i][j])
lx[i]=w[i][j];
}
for(i=1;i<=mcount;i++)
{
for(j=1;j<=hcount;j++)
slack[j]=MAX;
while(1)
{
memset(visitx,0,sizeof(visitx));
memset(visity,0,sizeof(visity));
if(dfs(i))/*寻找完备匹配*/
break;
d=MAX;
for(j=1;j<=hcount;j++)
if(!visity[j] && d>slack[j])
d=slack[j]; /*找出最小的d值来更新顶点标号*/
for(j=1;j<=mcount;j++)
{
if(visitx[j]) lx[j]-=d;
if(visity[j]) ly[j]+=d;
else slack[j]-=d;
}
}
}
ans=0;
for(i=1;i<=mcount;i++)
ans-=(lx[i]+ly[i]);  /*结果加的权值需要再取反,才为结果*/
return ans;
}
int main()
{
int i,j,n,m;
while(scanf("%d%d",&n,&m),n!=0||m!=0)
{
getchar();  /*此处要小心*/
hcount=mcount=0;
for(i=0;i<n;i++)
gets(map[i]);
for(i=0;i<n;i++)
for(j=0;j<m;j++)
{/*不知为何此处记录的mcount和hcount从1开始存就正确,从0开始就错误,很纠结*/
if(map[i][j]=='m')
{
mcount++;
man[mcount].r=i;
man[mcount].c=j;
}
else if(map[i][j]=='H')
{
hcount++;
home[hcount].r=i;
home[hcount].c=j;
}
}
for(i=1;i<=mcount;i++)  /*KM算法求的是最大权的完美匹配,所以将权值取反,权值是距离*/
for(j=1;j<=hcount;j++)
w[i][j]=-(abs(man[i].r-home[j].r)+abs(man[i].c-home[j].c));
printf("%d/n",KM());
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: