poj 2195 Going Home 二分图最大权匹配
求二分图最小权匹配,转换成最大权匹配。
方法一,初始化边e为负的权值,取反输出答案;
方法二,lx[]用min初始化,求slack时用max, 从最小值逐渐增大。
感觉二分图最大权匹配的思想与差分约束求最大值有些像。 差分约束求最大值一直保持d[u] <= d[v]+ e(u, v),而最大权匹配则是通过顶标,先从最大的权值边匹配,若不能构成完全匹配,就在相等子图里添加边。都是从最大值开始减少,当满足所有条件时,就是所求的最大值
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int N = 105;
const int INF = 1000000000;
int E[N][N];
bool map[N][N];
bool vis_x[N], vis_y[N];
int lx[N], ly[N];
int match[N];
char maze[N][N];
int row, col;
int man_count, house_count;
int slack;
int abs(int a)
{
if (a < 0)
return -a;
return a;
}
bool dfs(int u, int n)
{
int tmp;
vis_x[u] = true;
for (int i = 0; i < n; i++)
{
if (!vis_y[i])
{
tmp = lx[u] + ly[i] - E[u][i];
if (tmp == 0)
{
vis_y[i] = true;
if (match[i] == -1 || dfs(match[i], n))
{
match[i] = u;
return true;
}
}
else
slack = max(tmp, slack);
}
}
return false;
}
//最小权匹配
void KM(int n)
{
for (int i = 0; i < n; i++)
{
lx[i] = INF; //-inf
ly[i] = 0;
for (int j = 0; j < n; j++)
lx[i] = min(lx[i], E[i][j]); //若求最大权匹配用max
}
memset(match, -1, sizeof(match));
for (int i = 0; i < n; i++)
{
while (1)
{
memset(vis_x, false, sizeof(vis_x));
memset(vis_y, false, sizeof(vis_y));
slack = -INF; //inf
if (dfs(i, n))
break;
for (int i = 0; i < n; i++)
{
if (vis_x[i]) lx[i] -= slack;
if (vis_y[i]) ly[i] += slack;
}
}
}
}
int main()
{
while (scanf("%d%d", &row, &col))
{
if (row == 0 && col == 0)
break;
memset(E, 0, sizeof(E));
man_count = 0;
house_count = 0;
for (int i = 0; i < row; i++)
{
getchar();
for (int j = 0; j < col; j++)
scanf("%c", &maze[i][j]);
}
//构图
for (int i = 0; i < row; i++)
for (int j = 0; j < col; j++)
if (maze[i][j] == 'm')
{
house_count = 0;
for (int a = 0; a < row; a++)
for (int b = 0; b < col; b++)
if (maze[a][b] == 'H')
E[man_count][house_count++] = abs(a-i) + abs(b-j);
man_count++;
}
KM(man_count);
int ans = 0;
for (int i = 0; i < man_count; i++)
ans += E[match[i]][i];
printf("%d\n", ans);
}
return 0;
}
浙公网安备 33010602011771号