题目: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 0x7f7f7f7f
int t, n, m;
int x[MAX], y[MAX]; // 每次查找完美匹配时候,区分使用过 -- 1, 未使用过 -- 0
int 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;
}