题目大意:在一个网格里面有n个小男人和n个房子,现在想让每个小男人都有一个房子住,不过每个人移动一下都需要花费¥1,现在求出来最小的总花费。ps:可以认为网格的每个点都是很大的广场并且容纳所有的人,人可以走在有房子的点但是不进入房子。
分析:人-房子,很完美的带全都最小值匹配啊,人到一个房子的花费就是他们之间的曼哈顿距离,用这些距离构造一个二分图,然后用KM算法求出来最小费。下面是KM算法
***********************************************************************************************************************************************************************************
#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
#include<math.h>
using namespace std;
const int MAXN = 107;
const int oo = 1e9+7;
struct point{int x, y;}man[MAXN], house[MAXN];
char G[MAXN][MAXN];
int w[MAXN][MAXN], slack[MAXN], Nx, Ny;
int dx[MAXN], dy[MAXN], Ly[MAXN];
bool vx[MAXN], vy[MAXN];
void InIt()
{
Nx = Ny = 0;
for(int i=1; i<MAXN; i++)
{
dx[i] = -oo;
dy[i] = 0;
}
}
bool Find(int i)
{
vx[i] = true;
for(int j=1; j<=Ny; j++)
{
if(!vy[j] && w[i][j] == dx[i]+dy[j])
{
vy[j] = true;
if(!Ly[j] || Find(Ly[j]))
{
Ly[j] = i;
return true;
}
}
else if(!vy[j])
slack[j] = min(slack[j], dx[i]+dy[j]-w[i][j]);
}
return false;
}
int KM()
{
int i, j;
memset(Ly, 0, sizeof(Ly));
for(i=1; i<=Nx; i++)
{
for(j=1; j<=Ny; j++)
slack[j] = oo;
while(true)
{
memset(vx, false, sizeof(vx));
memset(vy, false, sizeof(vy));
if(Find(i) == true)
break;
int d = oo;
for(j=1; j<=Ny; j++)
{
if(!vy[j] && d > slack[j])
d = slack[j];
}
for(j=1; j<=Nx; j++)
{
if(vx[j])
dx[j] -= d;
}
for(j=1; j<=Ny; j++)
{
if(vy[j])
dy[j] += d;
else
slack[j] -= d;
}
}
}
int sum = 0;
for(i=1; i<=Ny; i++)
sum += w[Ly[i]][i];
return -sum;
}
int main()
{
int M, N;
while(scanf("%d%d", &M, &N), M+N)
{
int i, j;
InIt();
for(i=0; i<M; i++)
scanf("%s", G[i]);
for(i=0; i<M; i++)
for(j=0; j<N; j++)
{
if(G[i][j] == ‘m‘)
{
Nx++;
man[Nx].x = i;
man[Nx].y = j;
}
if(G[i][j] == ‘H‘)
{
Ny++;
house[Ny].x = i;
house[Ny].y = j;
}
}
for(i=1; i<=Nx; i++)
for(j=1; j<=Ny; j++)
{
w[i][j] = fabs(man[i].x-house[j].x) + fabs(man[i].y-house[j].y);
w[i][j] = -w[i][j];
dx[i] = max(dx[i], w[i][j]);
}
printf("%d\n", KM());
}
return 0;
}