POJ - 2195 Going Home (构图 最大匹配KM算法)

来源:互联网 发布:java常见的设计模式 编辑:程序博客网 时间:2024/06/06 21:38

题目:http://poj.org/problem?id=2195

题意:

‘.’ ,‘m’, ‘H’-> 通路, 人, 房子

让所有人回到房子里,使得总路径最小

分析:

由题意可知,人与房的距离为权值,人和房子分别为x、y两个集合,所以可以转换成二部图【权值最大】匹配问题,只要把路径取【负值】,最后结果再取负即可

构图之后直接使用KM算法求解

KM算法主要流程:

1)初始化,房子集合的顶标ly全为0,人集合的顶标lx全为最大值(人到其他房子距离),确保lx[i] + ly[j] >= Edge[i][j](第i人到第j房的距离(负值))

2)寻找完备匹配(一个集合匹配完全),使用匈牙利算法

3)寻找失败后,更新匹配,更新顶标

4)重复2~3,得出结果

代码:

#include <stdio.h>#include <iostream>#include <string.h>#include <string>#include <math.h>#include <algorithm>#include <queue>#include <stack>#include <vector>#include <map>using namespace std;#define MAX 101#define MIN -1e9#define INF 0x7f7f7f7fint t, n, m;int x[MAX], y[MAX]; // 每次查找完美匹配时候,区分使用过 -- 1, 未使用过 -- 0int Edge[MAX][MAX]; // Edge【i】【j】 第i个人到第j个房子的距离(负值)int lx[MAX], ly[MAX]; // 顶标int linky[MAX]; // 匹配记录int path(int u) // 增广路判断 , 匈牙利算法{x[u] = 1;for(int v = 1; v<=m; v++) // 每个x遍历所有y{if(y[v] == 1) continue;if(lx[u] + ly[v] == Edge[u][v]) // 可行顶标{y[v] = 1;if(linky[v] == -1 || path(linky[v])) // y为匹配 || 更改匹配y的x成功{linky[v] = u;return 1;}}}return 0;}void KM(){memset(linky, -1, sizeof(linky)); // 初始化memset(ly, 0, sizeof(ly)); // y的顶标for(int i = 1; i<=n; i++) // x的顶标{lx[i] = -INF;for(int j = 1; j<=m; j++){lx[i] = max(lx[i], Edge[i][j]);}}for(int i = 1; i<=n; i++) // 所有x都有增广路 -> 完美匹配{while(1){memset(x, 0, sizeof(x));memset(y, 0, sizeof(y));if(path(i)) break; // 增广路成功int d = INF; // 失败后,更换差值最小的边for(int j = 1; j<=n; j++) // 对于已经使用的x, 在所有为使用的y中查找新边,且更换的差值最小{if(x[j] == 1){for(int k = 1; k<=m; k++){if(y[k] == 0 && d > lx[j]+ly[k]-Edge[j][k]){d = lx[j] + ly[k] - Edge[j][k];}}}}for(int j = 1; j<=n; j++) // x的顶标-d,确保lx[i] + ly[j] >= Edge[i][j]{if(x[j] == 1) lx[j] -= d;}for(int j = 1; j<=m; j++) <span style="font-family: Arial, Helvetica, sans-serif;"> // y的顶标+d,确保lx[i] + ly[j] >= Edge[i][j]</span>{if(y[j] == 1) ly[j] += d;}}}int ans = 0;for(int i = 1; i<=m; i++){if(linky[i]!=-1) ans += Edge[linky[i]][i];}printf("%d\n", -ans);}struct MAN{int x, y;}man[MAX*MAX];struct HUOSE{int x, y;}house[MAX*MAX];int main(){int i, j;//freopen("a.txt", "r", stdin);int nt, mt;while(scanf("%d%d", &nt, &mt) && nt + mt){char mapt[MAX][MAX];for(i = 0; i<nt; i++){scanf("%s", mapt[i]);}n = m = 0;for(i = 0; i<nt; i++){for(j = 0; j<mt; j++){if(mapt[i][j] == 'm'){n++;man[n].x = i;man[n].y = j;}else if(mapt[i][j] == 'H'){m++;house[m].x = i;house[m].y = j;}}}for(i = 1; i<=n; i++){for(j = 1; j<=m; j++){Edge[i][j] = -(abs(man[i].x - house[j].x)+abs(man[i].y - house[j].y));}}KM();}return 0;}


0 0