1. 程式人生 > >POJ 2195 Going Home(最小權匹配、KM演算法)

POJ 2195 Going Home(最小權匹配、KM演算法)

題目連結:
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; }