链接:http://poj.org/problem?id=2195
有p个人到p个房间里去,一个房间只能住一个人。一个人没移动一个单位,花费1$,求所有人入住房间所需的最小花费。
二分图的最优匹配,用KM算法求解。不过这里是求最小的匹配,所以顶标要初始化为负的,然后输出也要取反。
//man和house数组的下标从1开始就正确,从0开始就错误,无语!
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
#define MAXN 105
#define INF 1<<30-1
char map[MAXN][MAXN];
int dmap[MAXN][MAXN];
int mx[MAXN],hx[MAXN];
int my[MAXN],hy[MAXN];
int mnum,hnum;
int lx[MAXN],ly[MAXN];
int linky[MAXN];
int vistx[MAXN],visty[MAXN];
int slack;
void init(int n,int m)
{
int i,j;
mnum=0;
hnum=0;
for(i=0;i<n;i++)
scanf("%s",map[i]);
for(i=0;i<n;i++)
for(j=0;j<m;j++)
{
if(map[i][j]=='m')
{
mnum++; //mnum先++,下标从1开始,对了
mx[mnum]=i;
my[mnum]=j;
//num++,后加,一直错啊
}
if(map[i][j]=='H')
{
hnum++;
hx[hnum]=i;
hy[hnum]=j;
}
}
for(i=1;i<=mnum;i++)
for(j=1;j<=hnum;j++)
dmap[i][j]=-(abs(mx[i]-hx[j])+abs(my[i]-hy[j]));
}
int find(int x)
{
int y;
vistx[x]=1;
for(y=1;y<=hnum;y++)
{
if(!visty[y])
{
int t=lx[x]+ly[y]-dmap[x][y];
if(t==0)
{
visty[y]=1;
if(linky[y]==0||find(linky[y]))
{
linky[y]=x;
return 1;
}
}
else if(slack>t)
slack=t;
}
}
return 0;
}
void KM()
{
int i,j,x;
int ans;
memset(linky,0,sizeof(linky));
memset(lx,100,sizeof(lx)); //注意lx的初始化
//如果是200,初始化的就是负数,如果是0,就是0
//printf("%d\n",lx[1]);
memset(ly,0,sizeof(ly));
for(i=1;i<=mnum;i++)
for(j=1;j<=hnum;j++)
if(dmap[i][j]>lx[i])
lx[i]=dmap[i][j];
for(x=1;x<=mnum;x++)
{
//memset(slack,100,sizeof(slack));
for(;;)
{
memset(vistx,0,sizeof(vistx));
memset(visty,0,sizeof(visty));
slack=INF;
if(find(x))
break;
//d=INF;
//for(i=1;i<=hnum;i++)
//if(!visty[i]&&d>slack[i])
//d=slack[i];
for(i=1;i<=mnum;i++)
if(vistx[i])
lx[i]-=slack;
for(i=1;i<=hnum;i++)
if(visty[i])
ly[i]+=slack;
}
}
ans=0;
for(i=1;i<=hnum;i++)
ans+=dmap[linky[i]][i];
printf("%d\n",-ans);
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
if(!n&&!m)return 0;
init(n,m);
KM();
}
return 0;
}