题目链接:POJ-2195 Going Home
题意
给出$N$行$M$列的网格,'m'表示人,'H'表示房子,且'm'和'H'的数量相同,一个人只能进一间房子,一间房子也只能给一个人进去,人可走上下左右四个方向,现在要让所有人进入房子,求最短路径和。
思路
这是一个二分图带权最小匹配问题,可直接用最小费用最大流求解。
源点向人各连一条容量为1,费用为0的边,每个人向每间房子连容量为1,费用为距离的边,每间房子向汇点连容量为1,费用为0的边,然后跑最小费用最大流即可。
代码实现
#include <iostream> #include <cstdio> #include <vector> #include <queue> #include <cstdlib> #include <cstring> #define N 5000 using namespace std; typedef pair<int, int> P; const int INF = 0x3f3f3f3f; struct Edge { int to, cap, cost, rev; Edge(int t, int c, int cc, int r) :to(t), cap(c), cost(cc), rev(r){} }; int V; vector<Edge> G[N]; int h[N]; int dist[N]; int prevv[N]; int preve[N]; void addedge(int from, int to, int cap, int cost) { G[from].push_back(Edge(to, cap, cost, G[to].size())); G[to].push_back(Edge(from, 0, -cost, G[from].size() - 1 )); } int min_cost_flow(int s, int t) { int res = 0; fill(h, h + V, 0); while (true) { priority_queue<P, vector<P>, greater<P> >q; fill(dist, dist + V, INF); dist[s] = 0; q.push(P(0, s)); while (!q.empty()) { P p = q.top(); q.pop(); int v = p.second; if (dist[v] < p.first)continue; for (int i = 0; i < G[v].size(); i++) { Edge &e = G[v][i]; if (e.cap > 0 && dist[e.to] > dist[v] + e.cost + h[v] - h[e.to]) { dist[e.to] = dist[v] + e.cost + h[v] - h[e.to]; prevv[e.to] = v; preve[e.to] = i; q.push(P(dist[e.to], e.to)); } } } if (dist[t] == INF) break; for (int j = 0; j < V; j++) h[j] += dist[j]; int d = INF; for (int x = t; x != s; x = prevv[x]) d = min(d, G[prevv[x]][preve[x]].cap); if (d == 0) break; res += d * h[t]; for (int x = t; x != s; x = prevv[x]) { Edge &e = G[prevv[x]][preve[x]]; e.cap -= d; G[x][e.rev].cap += d; } } return res; } int main() { int n, m; while (~scanf("%d %d", &n, &m) && n && m) { vector<P> hou, man; char ch; for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { scanf(" %c", &ch); if (ch == 'H') hou.push_back(make_pair(i, j)); else if (ch == 'm') man.push_back(make_pair(i, j)); } } int cnt = hou.size(); V = 2 * cnt + 2; int s = 2 * cnt, t = 2 * cnt + 1; for (int i = 0; i < V + 10; i++) G[i].clear(); for (int i = 0; i < cnt; i++) { for (int j = 0; j < cnt; j++) { addedge(i, cnt + j, 1, abs(man[i].first - hou[j].first) + abs(man[i].second - hou[j].second)); } } for (int i = 0; i < cnt; i++) { addedge(s, i, 1, 0); addedge(cnt + i, t, 1, 0); } printf("%d ", min_cost_flow(s, t)); } return 0; }