POJ 2195 Going Home(最小權匹配、KM演算法)
阿新 • • 發佈:2019-01-28
題目連結:
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;
}