HDU-1533 Going Home

来源:互联网 发布:雀圣软件官方 编辑:程序博客网 时间:2024/05/16 17:48

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1533

题目大意:

给你一个N行M列的矩阵,其中“.”代表空地,“H”代表房子,“m”代表人,其中有n个房子和n个人。现在要求每个人进入一间房子,且人走一步需要支付1美元。

求最小需要花费多少美元才能让所有人都进入到房子中(每个人只能进入一间房子,每个房子只能容纳一个人)。

解题思路:

这道题其实就是二分图最优匹配的变形而已。

因为要求的其实是最小权值之和。而KM算法求的是最大权值之和。

为此,我们可以有两种方法来解决:

1.在建图的时候,将每条边的权值变为负数。然后lx[i]初始化为-INT_MAX,结果输出-ans,就可以得到最小权值。

2.在建图的时候,用一个相对的大数减去每个权值,比如现在边为6,8,10.我用100来减,得到的边为94,92,90.最后输出时,用100*n-ans也可以得到最小的权值。

方法1代码如下:

#include<iostream>#include<cstring>#include<climits>#include<cstdio>#include<algorithm>using namespace std;#define N 110char maps[N][N];int map[N][N];int lx[N], ly[N];int slack[N];int match[N];bool visitx[N], visity[N];int n;bool Hungary(int u){visitx[u] = true;for(int i = 0; i < n; ++i){if(visity[i])continue;else{if(lx[u] + ly[i] == map[u][i]){visity[i] = true;if(match[i] == -1 || Hungary(match[i])){match[i] = u;return true;}}elseslack[i] = min(slack[i], lx[u] + ly[i] - map[u][i]);}}return false;}void KM_perfect_match(){int temp;for(int i = 0; i < n; ++i) //初始化大负数lx[i] = -INT_MAX;memset(ly, 0, sizeof(ly));for(int i = 0; i < n; ++i)for(int j = 0; j < n; ++j)lx[i] = max(lx[i], map[i][j]);for(int i = 0; i < n; ++i){for(int j = 0; j < n; ++j)slack[j] = INT_MAX;while(1){memset(visitx, false, sizeof(visitx));memset(visity, false, sizeof(visity));if(Hungary(i))break;else{temp = INT_MAX;for(int j = 0; j < n; ++j)if(!visity[j])temp = min(temp, slack[j]);for(int j = 0; j < n; ++j){if(visitx[j])lx[j] -= temp;if(visity[j])ly[j] += temp;elseslack[j] -= temp;}}}}}int main(){int row, col, ans, numi, numj;while(scanf("%d%d", &row, &col) && (row + col)){n = ans = numi = numj = 0;memset(match, -1, sizeof(match));for(int i = 0; i < row; ++i){scanf("%*c");for(int j = 0; j < col; ++j){scanf("%c", &maps[i][j]);if(maps[i][j] == 'm')n++;}}for(int i = 0; i < row; ++i) //建图{for(int j = 0; j < col; ++j){if(maps[i][j] == 'm'){for(int k = 0; k < row; ++k){for(int l = 0; l < col; ++l){if(maps[k][l] == 'H')map[numi][numj++] = - (abs(k - i) + abs(l - j)); //变为负权边}}numi++;numj = 0;}}}KM_perfect_match();for(int i = 0; i < n; ++i)ans += map[match[i]][i];printf("%d\n", - ans); //结果取反}return 0;}
方法2代码如下:

#include<iostream>#include<cstring>#include<climits>#include<cstdio>#include<algorithm>using namespace std;#define N 110char maps[N][N];int map[N][N];int lx[N], ly[N];int slack[N];int match[N];bool visitx[N], visity[N];int n;bool Hungary(int u){visitx[u] = true;for(int i = 0; i < n; ++i){if(visity[i])continue;else{if(lx[u] + ly[i] == map[u][i]){visity[i] = true;if(match[i] == -1 || Hungary(match[i])){match[i] = u;return true;}}elseslack[i] = min(slack[i], lx[u] + ly[i] - map[u][i]);}}return false;}void KM_perfect_match(){int temp;memset(lx, 0, sizeof(lx));memset(ly, 0, sizeof(ly));for(int i = 0; i < n; ++i)for(int j = 0; j < n; ++j)lx[i] = max(lx[i], map[i][j]);for(int i = 0; i < n; ++i){for(int j = 0; j < n; ++j)slack[j] = INT_MAX;while(1){memset(visitx, false, sizeof(visitx));memset(visity, false, sizeof(visity));if(Hungary(i))break;else{temp = INT_MAX;for(int j = 0; j < n; ++j)if(!visity[j])temp = min(temp, slack[j]);for(int j = 0; j < n; ++j){if(visitx[j])lx[j] -= temp;if(visity[j])ly[j] += temp;elseslack[j] -= temp;}}}}}int main(){int row, col, ans, numi, numj;while(scanf("%d%d", &row, &col) && (row + col)){n = ans = numi = numj = 0;memset(match, -1, sizeof(match));for(int i = 0; i < row; ++i){scanf("%*c");for(int j = 0; j < col; ++j){scanf("%c", &maps[i][j]);if(maps[i][j] == 'm')n++;}}for(int i = 0; i < row; ++i) //建图{for(int j = 0; j < col; ++j){if(maps[i][j] == 'm'){for(int k = 0; k < row; ++k){for(int l = 0; l < col; ++l){if(maps[k][l] == 'H')map[numi][numj++] = 100 - (abs(k - i) + abs(l - j)); //大数减边}}numi++;numj = 0;}}}KM_perfect_match();for(int i = 0; i < n; ++i)ans += map[match[i]][i];printf("%d\n", 100 * n - ans); }return 0;}