1 //PKu 2195 回家 By Loli_con Enail : Loli_con@outlook.com
2 /*
3 题目叙述
4 =========
5 在一个网格图中,有n个人和n个房子。每一个单位时间,每个小人可以移动一个单位长度,无论水平还是竖直,到临近的点。对于每个人,每移动一步你需要花费1$,直到他移动到他的家,题目限制一个房子仅能住进一个人
6 你的任务是计算最小花费使得n个人都能住进不同的n个房子。
7 输入是一张地图,'.'表示空地,'H'表示房子,'m'表示人
8 你可以认为一个点是足够的大以至于可以同时站上去全部的n个人,当然,一个人也可以站在房子那个点而不进入。
9 =====================
10 输入叙述
11 =====================
12 多组测试数据。对于每组测试数据
13 第一行包括两个整数n和m,表示地图是n行m列的。
14 接下来n行每行m个字符,'.'、'H'、'm'含义如上。2<=n,m<=100,H<=100,
15 输入数据以0 0结束
16 =====================
17 输出叙述
18 =====================
19 对于每组测试数据输出一行一个整数最小花费
20 =====================
21 样例输入
22 =====================
23 2 2
24 .m
25 H.
26 5 5
27 HH..m
28 .....
29 .....
30 .....
31 mm..H
32 7 8
33 ...H....
34 ...H....
35 ...H....
36 mmmHmmmm
37 ...H....
38 ...H....
39 ...H....
40 0 0
41 =====================
42 样例输出
43 =====================
44 2
45 10
46 28
47 =====================
48 解题报告
49 =====================
50 最小费用最大流模版题,KM算法也可解
51 */
52 #include <iostream>
53 #include <cstdio>
54 #include <algorithm>
55 #include <string>
56 #include <cmath>
57 #include <cstring>
58 #include <queue>
59 #define Max 5005
60 #define inf 1<<28
61 using namespace std;
62 struct kdq
63 {
64 int x,y;
65 }human[Max],house[Max];
66 int n,m;
67 int S,T;//源点,汇点
68 int cost[Max/10][Max];//花费
69 int cap[Max/10][Max];//容量
70 int dis[Max];
71 int path[Max];
72 bool visit[Max];
73 int q[Max*10];
74
75 int spfa()//最短路
76 {
77 int i,j;
78 for(i=0;i<=T;i++)
79 dis[i]=inf,path[i]=-1,visit[i]=0;
80 dis[S]=0;
81 visit[S]=1;
82 int num=0,cnt=0;
83 q[num++]=S;
84 while(num>cnt)
85 {
86 int temp=q[cnt++];
87 visit[temp]=0;
88 for(i=0;i<=T;i++)
89 {
90 if(cap[temp][i]&&dis[temp]+cost[temp][i]<dis[i])
91 {
92 path[i]=temp;
93 dis[i]=dis[temp]+cost[temp][i];
94 if(!visit[i])
95 {
96 q[num++]=i;
97 visit[i]=1;
98 }
99 }
100 }
101 }
102 return dis[T]!=inf;
103 }
104
105 int minCost=0;
106 void getMaxFlow()//增广找最大流
107 {
108 int maxFlow=inf;
109
110 while(spfa())
111 {
112 int pre=T;
113 while(path[pre]!=-1)
114 {
115 maxFlow=min(maxFlow,cap[path[pre]][pre]);
116 pre=path[pre];
117 }
118 pre=T;
119 minCost+=dis[T]*maxFlow;//最小费用
120 while(path[pre]!=-1)//更新流
121 {
122 cap[path[pre]][pre]-=maxFlow;
123 cap[pre][path[pre]]+=maxFlow;
124 //minCost+=cost[path[pre]][pre]*maxFlow;
125 pre=path[pre];
126 }
127 }
128 cout<<minCost<<endl;
129 return ;
130 }
131
132 int getdis(kdq x,kdq y)//两个坐标之间的费用
133 {
134 return (abs(x.x-y.x)+abs(y.y-x.y));
135 }
136 void build_map(int numm,int numh)//建图
137 {
138 int i,j;
139 for(i=1;i<=numm;i++)//计算房子和人之间的费用
140 for(j=1;j<=numh;j++){
141 cost[i][j+numm]=getdis(human[i],house[j]);
142 cost[j+numm][i]=-cost[i][j+numm];//负费用用来回流
143 }
144 for(i=1;i<=numm;i++)//源点到每个人的cap,cost
145 cap[S][i]=1,cost[S][i]=0;
146 for(i=1;i<=numh;i++)//房子到汇点的cap,cost
147 cap[i+numm][T]=1;
148 for(i=1;i<=numm;i++)//每个人和房子之间的cap
149 for(j=1;j<=numh;j++)
150 cap[i][j+numm]=1;
151 }
152 int main()
153 {
154 int i,j,k,l;
155 char x;
156 while(scanf("%d%d",&n,&m),(n+m))
157 {
158 memset(cap,0,sizeof(cap));
159 memset(cost,0,sizeof(cost));
160
161 int numm=0,numh=0;
162 for(i=1;i<=n;i++)
163 for(j=1;j<=m;j++)
164 {
165 cin>>x;
166 if(x=='m'){
167 human[++numm].x=i;
168 human[numm].y=j;
169 }
170 if(x=='H'){
171 house[++numh].x=i;
172 house[numh].y=j;
173 }
174 }
175 S=0;
176 minCost=0;
177 T=numm+numh+1;//其实numm==numh。。。。
178 build_map(numm,numh);
179 getMaxFlow();
180 }
181 return 0;
182 }