[HDU] 1533 Going Home
题目可以转化为在一个两边点数相等的完全二分图上找出权值总和最小的匹配。
添加一个源点和一个汇点,汇点到每个人连一条容量为1,费用为0的边;每个房子到汇点连一条容量为1,费用为0的边;每个人到所有房子分别连一条容量为1,费用为人到房子距离的边。再在此图上做一次最小费用最大流即可。
View Code
#include <stdio.h>
#include <stdlib.h>
long b[10000],a[202][202][2];
int main (){
long pre[202],w[202],min[202],dh[202][2],dm[202][2],nh,nm,i,j,l,r,n,m,j1,j2,an,cost;
char c;
scanf("%ld%ld",&n,&m);
while (n|m){
nh=0;
nm=0;
for (i=0;i<n;i++){
for (j=0;j<m;j++){
scanf("%c",&c);
while (!(c=='m'||c=='H'||c=='.')){
scanf("%c",&c);
}
if (c=='m'){
nm++;
dm[nm][0]=i;
dm[nm][1]=j;
}
if (c=='H'){
nh++;
dh[nh][0]=i;
dh[nh][1]=j;
}
}
}
an=nh+nm+2;
for (i=0;i<an;i++){
for (j=0;j<an;j++){
a[i][j][0]=0;
a[i][j][1]=0;
}
}
for (i=1;i<=nm;i++){
a[0][i][0]=1;
a[0][i][1]=0;
a[nm+i][an-1][0]=1;
a[nm+i][an-1][1]=0;
for (j=1;j<=nm;j++){
a[i][nm+j][0]=1;
a[i][nm+j][1]=abs(dm[i][0]-dh[j][0])+abs(dm[i][1]-dh[j][1]);
}
}
for (j1=1;j1<=nm;j1++){
l=0;
r=0;
for (i=0;i<an;i++){
w[i]=0;
min[i]=1000000;
pre[i]=-1;
}
b[l]=0;
min[0]=0;
w[0]=1;
while (l<=r){
j=b[l];
for (i=0;i<an;i++){
if (a[j][i][0]){
j2=min[j]+a[j][i][1];
if (j2<min[i]){
min[i]=j2;
pre[i]=j;
if (w[i]==0){
r++;
b[r]=i;
w[i]=1;
}
}
}
}
w[j]=0;
l++;
}
i=an-1;
while (i>0){
a[i][pre[i]][0]=a[pre[i]][i][0];
a[pre[i]][i][0]=0;
a[i][pre[i]][1]=-a[pre[i]][i][1];
a[pre[i]][i][1]=0;
i=pre[i];
}
}
cost=0;
for (i=0;i<an;i++){
for (j=0;j<an;j++){
if (a[i][j][1]<0){
cost-=a[i][j][1];
}
}
}
printf("%ld\n",cost);
scanf("%ld%ld",&n,&m);
}
return 0;
}