当前位置: 代码迷 >> 综合 >> POJ 2195 Going Home(最小权匹配、KM算法)
  详细解决方案

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

热度:85   发布时间:2023-12-08 10:52:17.0

题目链接:
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方顶标全部减去常数dfor(int j = 0; j < n; j++) { if(visx[j]) lx[j] -= d; }//增广轨中Y方顶标全部增加dfor(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;
}
  相关解决方案