题意:有一张地图,m表示人,h表示房子,每个房子只能进一个人,房子数等于人数。为每个人分配一个房子,求每个人到每个房子的最短距离之和。
分析:最小费用流。从源点向每个人连一条容量为1的边,从每个人向每个房子连一条容量为1的边,费用为汉密尔顿距离,再从每个房子向汇点连一条容量为1,费用为0的边。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
const int N = 10005, M = 4 * 10005;
const int inf = 0x3f3f3f3f;
int h[N], e[M], ne[M], w[M], cost[M], idx;
int d[N], incf[N], pre[N];
bool st[N];
int s, t, maxflow, res;
char g[105][105];
void add(int a, int b, int z, int c)
{
e[idx] = b, w[idx] = z, cost[idx] = c, ne[idx] = h[a], h[a] = idx++;
e[idx] = a, w[idx] = 0, cost[idx] = -c, ne[idx] = h[b], h[b] = idx++;
}
bool spfa()
{
queue<int> q;
q.push(s);
memset(d, 0x3f, sizeof d);
memset(st, 0, sizeof st);
d[s] = 0, st[s] = true;
incf[s] = 1 << 30;
while (q.size())
{
int u = q.front();
q.pop();
st[u] = false;
for (int i = h[u]; i != -1; i = ne[i])
{
int j = e[i];
if (!w[i]) continue;
if (d[j] > d[u] + cost[i])
{
d[j] = d[u] + cost[i];
incf[j] = min(incf[u], w[i]);
pre[j] = i;
if (!st[j])
{
st[j] = true;
q.push(j);
}
}
}
}
if (d[t] == 0x3f3f3f3f) return false;
return true;
}
void update()
{
int u = t;
while (u != s)
{
int i = pre[u];
w[i] -= incf[t];
w[i ^ 1] += incf[t];
u = e[i ^ 1];
}
maxflow += incf[t];
res += d[t] * incf[t];
}
int n, m;
int num(int i, int j)
{
return i * m + j;
}
int get(int num1, int num2)
{
int x1 = num1 % m, y1 = num1 / m;
int x2 = num2 % m, y2 = num2 / m;
int dist = abs(x1 - x2) + abs(y1 - y2);
return dist;
}
int main()
{
while (scanf("%d%d", &n, &m) != EOF)
{
if (n == 0 && m == 0) break;
memset(h, -1, sizeof h), idx = 0;
res = 0, maxflow = 0;
scanf("%d%d", &n, &m);
for (int i = 0; i < n; ++i) scanf("%s", g + i);
s = n * m + 1, t = n * m + 2;
vector<int> p;
vector<int> hou;
//源点向人连边
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
{
if (g[i][j] == 'm')
{
p.push_back(num(i, j));
add(s, num(i, j), 1, 0);
}
if (g[i][j] == 'H')
{
hou.push_back(num(i, j));
}
}
//人向房子连边
for (int i = 0; i < p.size(); ++i)
{
for (int j = 0; j < hou.size(); ++j)
{
add(p[i], hou[j], 1, get(p[i], hou[j]));
}
}
//房子向汇点连边
for (int i = 0; i < hou.size(); ++i)
{
add(hou[i], t, 1, 0);
}
while (spfa()) update();
printf("%d
", res);
}
return 0;
}