题意:给定若干小人(用‘m’表示)和若干房子(用‘H’表示),已知小人的数量和房子的数量相同,一个房子只能容纳一个小人。问使得所有小人进入房子中走的最少距离是多少。
思路:最小费用最大流。小人和房子为顶点。每个小人i到房子j都连边,容量为1,费用为进入房子所需要的距离。最后再建立一个源点和一个汇点,源点连到所有的小人节点上,所有的房子节点连接到汇点上,这些边费用为0,容量为1.
#include <stdio.h>
#include <string.h>
#include <math.h>
#define INF 0x3fffffff
#define R 210
#define min(a,b) a<b?a:b
char s[R][R];
struct point{
int x,y;
}man[R],house[R];//存储小人和房子的坐标
struct edge{
int y,cost,cap,next;
}e[2*R*R+4*R];
int n,N,M,top,res;
int visited[R],dis[R],pre[R],index[R],first[R],q[2000000];
void add(int x,int y,int cost){//cost是边的费用,容量均为1
e[top].y = y;
e[top].cost = cost;
e[top].cap = 1;
e[top].next = first[x];
first[x] = top++;
e[top].y = x;
e[top].cost = -cost;
e[top].cap = 0;
e[top].next = first[y];
first[y] = top++;
}
int distance(int i,int j){//计算第i个小人到第j个房子的距离
return abs(man[i].x-house[j].x)+abs(man[i].y-house[j].y);
}
int spfa(){
int i,front,rear,now;
front = rear = -1;
for(i = 0;i<=n;i++)
dis[i] = INF;
memset(visited,0,sizeof(visited));
memset(pre,-1,sizeof(pre));
q[++rear] = 0;
dis[0] = 0;
while(front < rear){
now = q[++front];
visited[now] = 0;
for(i = first[now];i!=-1;i=e[i].next)
if(e[i].cap && dis[e[i].y]>dis[now]+e[i].cost){
dis[e[i].y] = dis[now]+e[i].cost;
if(!visited[e[i].y]){
q[++rear] = e[i].y;
visited[e[i].y] = 1;
}
pre[e[i].y] = now;
index[e[i].y] = i;
}
}
if(dis[n] == INF)
return 0;
return 1;
}
void update(){
int i,aug=INF;
for(i = n;i!=0;i=pre[i])
aug = min(aug,e[index[i]].cap);
res += aug*dis[n];
for(i = n;i!=0;i=pre[i]){
e[index[i]].cap -= aug;
e[index[i]^1].cap += aug;
}
}
int main(){
freopen("a.txt","r",stdin);
while(scanf("%d %d\n",&N,&M) &&N&&M){
int i,j,a,b;
memset(first,-1,sizeof(first));
a = b = res = top = 0;
for(i = 0;i<N;i++)
scanf("%s",s[i]);
for(i = 0;i<N;i++)
for(j = 0;j<M;j++)
if(s[i][j]=='m'){
man[++a].x = i;
man[a].y = j;
}else if(s[i][j] == 'H'){
house[++b].x = i;
house[b].y = j;
}
n = a;
for(i = 1;i<=n;i++)
for(j = 1;j<=n;j++)
add(i,j+n,distance(i,j));//小人和房子之间的边
for(i = 1;i<=n;i++){
add(0,i,0);
add(n+i,2*n+1,0);//添加超级源点和汇点
}
n = 2*n+1;
while(spfa())
update();
printf("%d\n",res);
}
return 0;
}