#include <iostream> #include <stdio.h> #include <string.h> #include <stdlib.h> //#define DEBUG #ifdef DEBUG #define debug(...) printf( __VA_ARGS__) #else #define debug(...) #endif #define N 101 #define MAX_INT 2000000 #define min(a, b) (a) < (b) ? (a) : (b) #define max(a, b) (a) > (b) ? (a) : (b) using namespace std; int weight[N][N]; /* X集合点到Y集合点的权重 */ int lx[N], ly[N]; /* 标号 */ char visitx[N], visity[N]; /* 用于深度搜索,同时可以标记S和T集合的点 */ int mat[N]; /* mat[y] = x,表示集合Y的y点的匹配为x */ int slack[N]; /* slack[x]保存相对与顶点x的最小al,初始时为无穷大, dfs寻找增广路径的时候不断更新,并且会越来越小 */ int nx, ny; struct position { int x; int y; }; /* 从顶点x开始深度优先寻找增广路径, 寻找的过程中同时更新slack数组 * 和mat数组,若找到返回1 * 若找不到返回0,此时visitx[x] = 1的点属于S集合 * visity[y] = 1的点属于T集合 */ int find(int x) { int y, t; visitx[x] = 1; debug("访问顶点X.%d\n", x); for (y = 1; y <= ny; y++) { if (!visity[y] && weight[x][y] != 0) { t = lx[x] + ly[y] - weight[x][y]; if (t != 0) { slack[x] = min(slack[x], t); } else { visity[y] = 1; debug("访问顶点Y.%d\n", y); if ((mat[y] == -1 || find(mat[y]))) { mat[y] = x; return 1; } debug("否定顶点Y.%d\n", y); } } } return 0; } int main() { int x, y, s, min_al, cost, m, n; char ch; struct position man[N], home[N]; while (cin>>m>>n, m) { //初始化l[v] memset(weight, 0, sizeof(weight)); memset(ly, 0, sizeof(ly)); memset(mat, -1, sizeof(mat)); for (x = 1; x <= N; x++) { lx[x] = -2000000; slack[x] = MAX_INT; } //输入数据 nx = ny = 0; for (x = 1; x <= m; x++) { for (y = 1; y <= n; y++) { cin >> ch; if (ch == 'm') { man[++nx].x = x; man[nx].y = y; } else if (ch == 'H') { home[++ny].x = x; home[ny].y = y; } } } //建图 for (x = 1; x <= nx; x++) { for (y = 1; y <= ny; y++) { weight[x][y] = abs(man[x].x-home[y].x) + abs(man[x].y-home[y].y); weight[x][y] *= -1; /* 题目要求最小权匹配,故把权值乘以-1,转变成求最大权匹配 */ lx[x] = max(lx[x], weight[x][y]); /* lx[x]初始化为最大权值 */ } } //给每个顶点寻找增广路径,若找不到就重新标号 for (s = 1; s <= nx;) { for (;;) { memset(visitx, 0, sizeof(visitx)); memset(visity, 0, sizeof(visity)); if (find(s)) { s++; debug("增广路径找到...\n"); break; } debug("从顶点%d找不到增广路径, 更新顶点标号...\n", s); //在S集合中找最小的lx[x]+ly[y]-weight[x][y], 借助slack数组 min_al = MAX_INT; for (x = 1; x <= nx; x++) { if (visitx[x] && slack[x] < min_al) { min_al = slack[x]; } } //更新S集合和T集合顶点标号 for (x = 1; x <= nx; x++) { if (visitx[x]) lx[x] -= min_al; } for (y = 1; y <= ny; y++) { if (visity[y]) ly[y] += min_al; } } } //打印匹配结果 for (y = 1; y <= ny; y++) { if (mat[y] != -1) { debug("X.%d-->Y.%d %d\n", mat[y], y, weight[mat[y]][y]); } } //计算最大权 cost = 0; for (x = 1; x <= nx; x++) { cost += lx[x]; } for (y = 1; y <= ny; y++) { cost += ly[y]; } printf("%d\n", -cost); } return 0; }