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

来源:互联网 发布:js 中文 unicode 编辑:程序博客网 时间:2024/06/04 23:20

题目链接:
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;}
0 0
原创粉丝点击