题意:有一张地图,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\n", res);
	}
	

	return 0;
}