题意:给定若干小人(用‘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; }