POJ 2195:Going Home(最小费用最大流)
http://poj.org/problem?id=2195
题意:有一个地图里面有N个人和N个家,每走一格的花费是1,问让这N个人分别到这N个家的最小花费是多少。
思路:通过这个题目学了最小费用最大流。最小费用最大流是保证在流量最大的情况下,使得费用最小。
建图是把S->人->家->T这些边弄上形成一个网络,边的容量是1(因为一个人只能和一个家匹配),边的费用是曼哈顿距离,反向边的费用是-cost。
算法的思想大概是通过SPFA找增广路径,并且找的时候费用是可以松弛的。当找到这样一条增广路就进行更新。注意这里的费用是单位流量的费用。反向边权为-cost是因为悔棋的时候费用要增加cost。
1 #include <cstdio> 2 #include <cstring> 3 #include <queue> 4 #include <vector> 5 #include <algorithm> 6 using namespace std; 7 #define N 205 8 #define INF 0x3f3f3f3f 9 struct node { 10 int x, y; 11 node () {} 12 node (int x, int y) : x(x), y(y) {} 13 }; 14 struct Edge { 15 int cap, u, v, cost; 16 Edge() {} 17 Edge(int u, int v, int cap, int cost) : u(u), v(v), cap(cap), cost(cost) {} 18 }edge[N*N]; 19 vector<node> p, h; 20 vector<int> G[N]; 21 int tot, dis[N], inq[N], pre[N], S, T; 22 23 void AddEdge(int u, int v, int cap, int cost) { 24 edge[tot] = Edge(u, v, cap, cost); 25 G[u].push_back(tot++); 26 edge[tot] = Edge(v, u, 0, -cost); // 表示反向增广(悔棋)的时候费用增加cost 27 G[v].push_back(tot++); 28 } 29 30 int CalDis(int x1, int y1, int x2, int y2) { 31 return abs(x1 - x2) + abs(y1 - y2); 32 } 33 34 bool SPFA() { 35 memset(inq, 0, sizeof(inq)); 36 memset(dis, INF, sizeof(dis)); 37 queue<int> que; que.push(S); 38 dis[S] = 0; inq[S] = 1; 39 while(!que.empty()) { 40 int u = que.front(); que.pop(); inq[u] = 0; 41 for(int i = 0; i < G[u].size(); i++) { 42 Edge &e = edge[G[u][i]]; 43 if(e.cap > 0 && dis[e.v] > e.cost + dis[u]) { 44 // 当可以增广并且费用可以松弛的时候 45 dis[e.v] = e.cost + dis[u]; 46 pre[e.v] = G[u][i]; // 记录路径 47 if(inq[e.v]) continue; 48 que.push(e.v); inq[e.v] = 1; 49 } 50 } 51 } 52 return dis[T] < INF; // 返回是否有增广路径 53 } 54 55 void MFMC(int &mincost, int &maxflow) { 56 int ans = 0, flow = INF, p; 57 // 从汇点沿着此次增广的路径往回走,当找到源点的时候退出 58 for(int u = T; u; u = edge[p].u) { 59 p = pre[u]; // 找增广的流量 60 if(edge[p].cap < flow) flow = edge[p].cap; 61 } 62 for(int u = T; u; u = edge[p].u) { 63 p = pre[u]; 64 edge[p].cap -= flow; // 更新每条边的流量 65 edge[p^1].cap += flow; 66 ans += flow * edge[p].cost; // 费用 = 单位费用 * 流量 67 } 68 mincost += ans, maxflow += flow; 69 } 70 71 int main() { 72 int n, m; 73 char s[105]; 74 while(scanf("%d%d", &n, &m), n + m) { 75 p.clear(); h.clear(); 76 for(int i = 0; i < n; i++) { 77 scanf("%s", s); 78 for(int j = 0; j < m; j++) { 79 if(s[j] == 'H') h.push_back(node(i, j)); 80 if(s[j] == 'm') p.push_back(node(i, j)); 81 } 82 } 83 tot = 0; int sz1 = p.size(), sz2 = h.size(); 84 S = 0, T = sz1 + sz2 + 1; 85 for(int i = 0; i <= T; i++) G[i].clear(); 86 for(int i = 0; i < sz1; i++) // S到man 87 AddEdge(S, i + 1, 1, 0); 88 for(int i = 0; i < sz2; i++) // house到T 89 AddEdge(i + 1 + sz1, T, 1, 0); 90 for(int i = 0; i < sz1; i++) { 91 for(int j = 0; j < sz2; j++) { 92 int c = CalDis(p[i].x, p[i].y, h[j].x, h[j].y); 93 AddEdge(i + 1, j + 1 + sz1, 1, c); 94 } 95 } 96 97 int mincost = 0, maxflow = 0; 98 while(SPFA()) MFMC(mincost, maxflow); 99 printf("%d\n", mincost); 100 } 101 return 0; 102 }