题目链接:
POJ 2195 Going Home
题意:
给出一个r*c的矩阵,字母H代表房屋,字母m代表客人,房屋的数量和客人的数量相同。每间房只能住一个人。求这些客人全部住进客房的最少移动步数?
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string>
#include <climits>
#include <queue>
#include <map>
#include <set>
#include <iostream>
using namespace std;
const int MAX_N = 400;
//求最大(小)权匹配时图的级别一般为10^2,所以用邻接矩阵存图
int r, c, n, m;
char s[MAX_N][MAX_N];
int match[MAX_N], visx[MAX_N], visy[MAX_N], lx[MAX_N], ly[MAX_N], w[MAX_N][MAX_N], slack[MAX_N];
struct Pos{
int x,y;
}house[MAX_N*MAX_N],host[MAX_N*MAX_N];
inline bool dfs(int x)
{
visx[x] = 1;
for(int y = 0; y < m; y++){
if(visy[y]) continue;
int tmp = lx[x] + ly[y] - w[x][y];
if( tmp == 0 ){
visy[y] = 1;
if(match[y] == -1 || dfs(match[y])){
match[y] = x;
return true; //找到增广轨
}
}else {
slack[y] = min(slack[y], tmp);
}
}
return false;
//没有找到增广轨,说明顶点X没有对应的匹配,
//与完备匹配(相等子图的3完备匹配)不符
}
inline int KM()
{
memset(match, -1, sizeof(match));
memset(ly, 0, sizeof(ly));
for(int i = 0; i < n; i++){
lx[i] = INT_MIN;
for(int j = 0; j < m; j++){
lx[i] = max(lx[i], w[i][j]);
}
}
for(int i = 0; i < n; i++){
//初始边的松弛值为最大
for(int j = 0; j < m; j++){ slack[j] = INT_MAX; }
while(1){
memset(visx, 0, sizeof(visx));
memset(visy, 0, sizeof(visy));
if(dfs(i)) break; //找到增广轨,则该点增广完成,进入下一点增广
//没有找到增广轨需要改变顶标使图中可行边数量增加
int d = INT_MAX;
for(int j = 0; j < m; j++){
if( !visy[j] ) d = min(d, slack[j]);
}
//增广轨(增广过程中遍历到)中X方顶标全部减去常数d
for(int j = 0; j < n; j++) { if(visx[j]) lx[j] -= d; }
//增广轨中Y方顶标全部增加d
for(int j = 0; j < m; j++) {
if(visy[j]) ly[j] += d;
else slack[j] -= d; //不在增广轨中的顶点Y
}
}
}
int res = 0;
for(int j = 0; j < m; j++) {
if( match[j] != -1) res += w[match[j]][j];
}
return res;
}
int main()
{
while(~scanf("%d%d",&r, &c) && (r || c)){
n = m = 0;
for(int i = 0; i < r; i++) {
scanf("%s",s[i]);
for(int j = 0; j < c; j++){
if(s[i][j] == 'H'){
house[n].x = i;
house[n++].y = j;
}else if(s[i][j] == 'm') {
host[m].x = i;
host[m++].y = j;
}
}
}
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
int tmp = abs(house[i].x - host[j].x) + abs(house[i].y - host[j].y);
w[i][j] = -tmp; //求最小权匹配,将边权取反,然后求最大权匹配
}
}
int ans = KM();
printf("%d\n",-ans); //结果取相反数
}
return 0;
}