Description
On a grid map there are n little men and n houses. In each unit time, every little man can move one unit step, either horizontally, or vertically, to an adjacent point. For each little man, you need to pay a $1 travel fee for every step he moves, until he enters a house. The task is complicated with the restriction that each house can accommodate only one little man.
Your task is to compute the minimum amount of money you need to pay in order to send these n little men into those n different houses. The input is a map of the scenario, a '.' means an empty space, an 'H' represents a house on that point, and am 'm' indicates there is a little man on that point.
You can think of each point on the grid map as a quite large square, so it can hold n little men at the same time; also, it is okay if a little man steps on a grid with a house without entering that house.
Your task is to compute the minimum amount of money you need to pay in order to send these n little men into those n different houses. The input is a map of the scenario, a '.' means an empty space, an 'H' represents a house on that point, and am 'm' indicates there is a little man on that point.

You can think of each point on the grid map as a quite large square, so it can hold n little men at the same time; also, it is okay if a little man steps on a grid with a house without entering that house.
Input
There are one or more test cases in the input. Each case starts with a line giving two integers N and M, where N is the number of rows of the map, and M is the number of columns. The rest of the input will be N lines describing the map. You may assume both N and M are between 2 and 100, inclusive. There will be the same number of 'H's and 'm's on the map; and there will be at most 100 houses. Input will terminate with 0 0 for N and M.
Output
For each test case, output one line with the single integer, which is the minimum amount, in dollars, you need to pay.
Sample Input
2 2 .m H. 5 5 HH..m ..... ..... ..... mm..H 7 8 ...H.... ...H.... ...H.... mmmHmmmm ...H.... ...H.... ...H.... 0 0
Sample Output
2 10 28
今天仔细听一宁姐的讲解,终于明白dinic邻接表建边了,底下的注释很详细。。。
题意是在n*m的矩阵中中,m是人,H是房子,人和房子一样多,距离是两点之间的距离,就是abs(x1-x0)+abs(y1-y0),然后问所有人到房子的最小费用,建一个源点和汇点,将源点和人相连,容量为1,权值为0,然后将人和房子建边,容量为1,权值为人和房子之间的房子,最后将房子和汇点相连,容量为1,权值为0,。。。
#include <iostream>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#define oo 1<<28
#include<queue>
using namespace std;
int pre[1000];
int dis[1000];
int vist[1000];
int head[1000];
struct node
{
int u;
int v;//与u点相连的点
int f;//容量
int w;//权值
int next;//下一条边
}edge[110000];
struct s
{
int x;
int y;
}peo[1000],hou[1000];
int cnt=0;
int s,t;
void add(int u,int v,int f,int w)
{
edge[cnt].u=u;
edge[cnt].v=v;
edge[cnt].f=f;
edge[cnt].w=w;
edge[cnt].next=head[u];
head[u]=cnt++;
edge[cnt].u=v;
edge[cnt].v=u;
edge[cnt].f=0;
edge[cnt].w=-w;//反向的权值为正向的相反数
edge[cnt].next=head[v];
head[v]=cnt++;
}
void init()
{
memset(pre,-1,sizeof(pre));
memset(vist,0,sizeof(vist));
for(int i=0;i<=t;i++)
{
dis[i]=oo;
}
}
int spfa()
{
int i;
init();
queue<int>q;
dis[s]=0;
vist[s]=1;
q.push(s);
while(!q.empty())//将相邻点进行松弛,直到队列为空
{
int u=q.front();//取出队头
q.pop();
i=head[u];
vist[u]=0;
while(i!=-1)
{
int w=edge[i].w;
int v=edge[i].v;
if(edge[i].f>0&&dis[v]>dis[u]+w)//判断是否可以更新
{
dis[v]=dis[u]+w;//改进s到v点的值
pre[v]=i;//记录此点的前驱
if(!vist[v])//由于距离变小了,如果v点松弛成功且v点不在队列里,因为v点有可能还能改进别的点
{
vist[v]=1;
q.push(v);
}
}
i=edge[i].next;
}
}
if(pre[t]==-1)//如果不在最短路中代表寻找失败
return 0;
return 1;
}
int Cost()
{
int ans=0;
while(spfa())//如果增广路寻找成功
{
int maxx=oo;
int p=pre[t];//初始化P指针
while(p!=-1)
{
maxx=min(edge[p].f,maxx);//求出此增广路上边的最小值
p=pre[edge[p].u];
}
p=pre[t];
while(p!=-1)
{
edge[p].f-=maxx;//正向减去
edge[p^1].f+=maxx;//反向增加
ans+=maxx*edge[p].w;//因为以单位计费,所以应当乘上流量
p=pre[edge[p].u];
}
}
return ans;
}
int main()
{
int n,m,d;
int i,j;
char str[1000][1000];
while(~scanf("%d%d",&n,&m)&&n&&m)
{
memset(head,-1,sizeof(head));
int num=0,num1=0;
for(i=0; i<n; i++)
{
getchar();
for(j=0; j<m; j++)
{
scanf("%c",&str[i][j]);
if(str[i][j]=='m')
{
add(0,++num,1,0);//将源点和人相连,容量为1,权值为0
peo[num].x=i;//记录人的坐标
peo[num].y=j;
}
if(str[i][j]=='H')
{
++num1;//记录房子的坐标和个数
hou[num1].x=i;
hou[num1].y=j;
}
}
}
//printf("%d %d\n",num,num1);
s=0,t=num+num1+1;
for(i=1;i<=num;i++)
{
for(j=1;j<=num1;j++)
{
d=abs(peo[i].x-hou[j].x)+abs(peo[i].y-hou[j].y);//两点之间的花费
add(i,j+num,1,d);//将人和房子之间建边,容量为1,权值为d
}
}
for(i=1;i<=num1;i++)
{
add(i+num,t,1,0);//将房子和汇点建边,容量为1,权值为0
}
printf("%d\n",Cost());
}
return 0;
}