Going Home
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 3140 Accepted Submission(s): 1602
Problem 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.
![](http://acm.hdu.edu.cn/data/images/1533.jpg)
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
这是一道最小距离最大流的例题,重点在构图,下面是两个算法,第一个是我自己写的,另一个是我参考的,目前我还属于半懂不懂,就不写题解了哈,下附代码
#include<iostream> #include<queue> #include<cstring> #include<climits> using namespace std; int n,m; char map[110][110]; struct Street { int flow,c,c_f,cost; }edge[220][220]; struct Man { int x,y; }man[110]; struct Home { int x,y; }home[110]; int abss(int a,int b) { if(a<b)return b-a; else return a-b; } int minn(int a,int b) { return a<b?a:b; } int pre[220]; bool isq[11000]; int dist[220]; int people,house,all; void init() { people=0,house=0; for(int i=0;i<n;i++) { for(int j=0;j<m;j++) { cin>>map[i][j]; if(map[i][j]=='m') { people++; man[people].x=i; man[people].y=j; } else if(map[i][j]=='H') { house++; home[house].x=i; home[house].y=j; } } } all=house+people+1; memset(edge,0,sizeof(edge)); for(int i=1;i<=people;i++) { edge[0][i].c=1;edge[0][i].c_f=1; edge[i+people][all].c=1;edge[i+people][all].c_f=1; for(int j=1;j<=house;j++) { int cost=abss(man[i].x,home[j].x)+abss(man[i].y,home[j].y); edge[i][house+j].cost=cost; edge[i][house+j].c=edge[i][house+j].c_f=1; edge[house+j][i].cost=-cost; } } } void spfa() { memset(pre,-1,sizeof(pre)); for(int i=0;i<=all;i++) { dist[i]=INT_MAX; isq[i]=false; } dist[0]=0; isq[0]=true; int temp; queue<int>q; q.push(0); while(!q.empty()) { temp=q.front(); q.pop(); isq[temp]=false; for(int i=0;i<=all;i++) { if(edge[temp][i].c_f&&edge[temp][i].cost+dist[temp]<dist[i]) { pre[i]=temp; dist[i]=dist[temp]+edge[temp][i].cost; if(!isq[i]) { isq[i]=true; q.push(i); } } } } } void MCMF() { spfa(); int flow=0,spend=0; while(pre[all]!=-1) { int temp=INT_MAX; for(int i=all;pre[i]!=-1;i=pre[i]) { temp=minn(temp,edge[pre[i]][i].c_f); } flow+=temp; spend+=temp*dist[all]; for(int i=all;pre[i]!=-1;i=pre[i]) { edge[pre[i]][i].c_f-=temp; edge[i][pre[i]].c_f+=temp; } spfa(); } cout<<spend<<endl; } int main() { while(cin>>n>>m,n+m) { init(); MCMF(); } return 0; }
#include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<climits> #include<algorithm> #include<queue> using namespace std; int n,m; const int N=250; const int M=10000; const int MAX=0xffffff; char coord[N][N];//坐标集 int pre[M];//存储前驱顶点 int dist[M];//存储到源点s的距离 int inq[M];//每个顶点是否在队列中的标志 int min_c_f;//记录增广路径中的残留容量 int vertex;//顶点数 int sum;//保存最小费用 struct element { int c;//容量 int f;//流 int c_f;//残留容量 int v;//价值 } G[N][N]; struct man//记录小矮人的坐标 { int x,y; } man[N]; struct house//记录房子的坐标 { int x,y; } house[N]; void init() { sum=0; int mcase,hcase;//记录有多少个小矮人和房子 mcase=hcase=0; for(int i=1; i<=m; i++)//输入过程 { for(int j=1; j<=n; j++) { cin>>coord[i][j]; if(coord[i][j]=='m')//记录小矮人的坐标 { mcase++; man[mcase].x=i; man[mcase].y=j; } if(coord[i][j]=='H')//记录房子的坐标 { hcase++; house[hcase].x=i; house[hcase].y=j; } } } vertex=mcase+hcase+1;//加入超源点0和超汇点,注意要+1,即抽象成网络流的结构 for(int u=0; u<=vertex; u++)//初始流为0,所以不用重构W(f); { for(int v=0; v<=vertex; v++) { G[u][v].c=G[v][u].c=0; G[u][v].c_f=G[v][u].c_f=0; G[u][v].f=G[v][u].f=0; G[u][v].v=G[v][u].v=MAX; } } for(int i=1; i<=mcase; i++) { G[0][i].v=0;//从超源点到各个小矮人之间的权值取为0 G[0][i].c=G[0][i].c_f=1;//从超源点到各个小矮人之间的容量取为1 for(int j=1; j<=hcase; j++) { int w=abs(house[j].x-man[i].x)+abs(house[j].y-man[i].y);//计算小矮人到每一个房子之间的距离 G[i][mcase+j].v=w;//将距离赋给对应的权值,注意第二个下标,即表示房子的下标为mcase+j~!! G[i][mcase+j].c=1;//容量取为1 G[i][mcase+j].c_f=G[i][mcase+j].c; G[mcase+j][vertex].v=0;//将从各个房子到超汇点之间的权值取为0,注意房子的下标为mcase+j G[mcase+j][vertex].c=G[mcase+j][vertex].c_f=1;//将从各个房子到超汇点之间的容量取为0,注意房子的下标为mcase+j } } } void SPFA(int s)//求最短路径的SPFA算法 { queue<int> Q; int u; for(int i=0; i<=vertex; i++)//初始化 { dist[i]=MAX; pre[i]=-1; inq[i]=0; } dist[s]=0; Q.push(s); inq[s] = 1; while(!Q.empty()) { u=Q.front(); Q.pop(); inq[u]=0; for(int i=0; i<=vertex; i++)//更新u的邻接点的dist[], pre[], inq[] { int v=i; if(G[u][v].c_f==0) // 表示(u,v)没有边 continue; if(G[u][v].v==MAX) G[u][v].v=-G[v][u].v; if(dist[v]>dist[u]+G[u][v].v)//松弛操作 { dist[v]=dist[u]+G[u][v].v; pre[v]=u; if(inq[v]==0) { Q.push(v); inq[v]=1; } } } } } void ford_fulkerson(int s,int t) { SPFA(s); while(pre[t]!=-1)//pre为-1表示没有找到从s到t的增广路径 { //cout<<dist[t]<<"^_^"<<endl; sum+=dist[t];//将这一条最短路径的值加进sum min_c_f=MAX; int u=pre[t], v=t;//计算增广路径上的残留容量 while(u!=-1) { if(min_c_f > G[u][v].c_f) min_c_f=G[u][v].c_f; v=u; u=pre[v]; } u=pre[t], v=t; while(u!=-1) { G[u][v].f+=min_c_f; //修改流 G[v][u].f=-G[u][v].f; G[u][v].c_f=G[u][v].c-G[u][v].f; //修改残留容量 G[v][u].c_f=G[v][u].c-G[v][u].f; v=u; u=pre[v]; } SPFA(s); } } int main() { while(cin>>m>>n,m||n) { init(); ford_fulkerson(0,vertex);//计算从超源点0到超汇点vertex之间的最小费用最大流 cout<<sum<<endl; } return 0; }