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
这个题目难在建图,就是把每一个人的位置,和每一个房子连起来,容量为1,费用为两个之间的距离。
然后就跑一个最小费用最大流就可以了。
#include <cstdio> #include <cstdlib> #include <queue> #include <vector> #include <iostream> #include <algorithm> #include <map> #include <cstring> #include <cmath> #include <string> #define inf 0x3f3f3f3f using namespace std; typedef long long ll; const int INF = 0x3f3f3f3f; const int maxn = 1000 + 10; struct edge { int u, v, c, f, cost; edge(int u, int v, int c, int f, int cost) :u(u), v(v), c(c), f(f), cost(cost) {} }; vector<edge>e; vector<int>G[maxn]; int a[maxn];//找增广路每个点的水流量 int p[maxn];//每次找增广路反向记录路径 int d[maxn];//SPFA算法的最短路 int inq[maxn];//SPFA算法是否在队列中 int s, t; void init() { for (int i = 0; i <= maxn; i++)G[i].clear(); e.clear(); } void add(int u, int v, int c, int cost) { e.push_back(edge(u, v, c, 0, cost)); e.push_back(edge(v, u, 0, 0, -cost)); int m = e.size(); G[u].push_back(m - 2); G[v].push_back(m - 1); } bool bellman(int s, int t, int& flow, long long & cost) { memset(d, inf, sizeof(d)); memset(inq, 0, sizeof(inq)); d[s] = 0; inq[s] = 1;//源点s的距离设为0,标记入队 p[s] = 0; a[s] = INF;//源点流量为INF(和之前的最大流算法是一样的) queue<int>q;//Bellman算法和增广路算法同步进行,沿着最短路拓展增广路,得出的解一定是最小费用最大流 q.push(s); while (!q.empty()) { int u = q.front(); q.pop(); inq[u] = 0;//入队列标记删除 for (int i = 0; i < G[u].size(); i++) { edge & now = e[G[u][i]]; int v = now.v; if (now.c > now.f && d[v] > d[u] + now.cost) //now.c > now.f表示这条路还未流满(和最大流一样) //d[v] > d[u] + e.cost Bellman 算法中边的松弛 { d[v] = d[u] + now.cost;//Bellman 算法边的松弛 p[v] = G[u][i];//反向记录边的编号 a[v] = min(a[u], now.c - now.f);//到达v点的水量取决于边剩余的容量和u点的水量 if (!inq[v]) { q.push(v); inq[v] = 1; }//Bellman 算法入队 } } } if (d[t] == INF)return false;//找不到增广路 flow += a[t];//最大流的值,此函数引用flow这个值,最后可以直接求出flow cost += (long long)d[t] * (long long)a[t];//距离乘上到达汇点的流量就是费用 for (int u = t; u != s; u = e[p[u]].u)//逆向存边 { e[p[u]].f += a[t];//正向边加上流量 e[p[u] ^ 1].f -= a[t];//反向边减去流量 (和增广路算法一样) } return true; } int MincostMaxflow(int s, int t, long long & cost) { cost = 0; int flow = 0; while (bellman(s, t, flow, cost));//由于Bellman函数用的是引用,所以只要一直调用就可以求出flow和cost return flow;//返回最大流,cost引用可以直接返回最小费用 } struct node { int x, y; node(int x=0,int y=0):x(x),y(y){} }; node peo[110], house[110]; char mp[110][110]; int main() { int n, m; while(cin>>n>>m) { init(); int cas = 0, tot = 0; if (n == 0 && m == 0) break; for (int i = 1; i <= n; i++) { cin >> mp[i] + 1; for(int j=1;j<=m;j++) { if (mp[i][j] == 'm') peo[++cas] = node(i, j); if (mp[i][j] == 'H') house[++tot] = node(i, j); } } s = 0, t = cas + tot + 1; for (int i = 1; i <= cas; i++) add(s, i, 1, 0); for (int i = 1; i <= tot; i++) add(cas + i, t, 1, 0); for(int i=1;i<=cas;i++) { for(int j=1;j<=tot;j++) { int cost = abs(peo[i].x - house[j].x) + abs(peo[i].y - house[j].y); add(i, j + cas, 1, cost); } } ll cost = 0; int ans = MincostMaxflow(s, t, cost); printf("%lld\n", cost); } return 0; }