[关键字]:图论 二分图最佳匹配
[题目大意]:给出n*m的地图,由几个H(房子)和m(人),求每个人都走到一个房子的最少需要的总步数。
//============================================================================
[分析]:这道题是二分图最大匹配的应用。先把边权取负然后就是求一个边权最大的匹配。利用KM算法求解,具体:http://blog.sina.com.cn/s/blog_625c774e0100hhh7.html
[代码]:
View Code
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int MAXN=110;
const int INF=0x7fffffff;
int n,m,house,man;
int xh[MAXN],yh[MAXN],xm[MAXN],ym[MAXN];
int a[MAXN][MAXN],slack[MAXN],lx[MAXN],ly[MAXN],match[MAXN];
bool vx[MAXN],vy[MAXN];
char map[MAXN][MAXN];
bool Init()
{
scanf("%d%d",&n,&m);
if (!n && !m) return 0;
for (int i=0;i<n;++i) scanf("%s",map[i]);
house=man=0;
for (int i=0;i<n;++i)
for (int j=0;j<m;++j)
{
if (map[i][j]=='H') xh[++house]=i,yh[house]=j;
if (map[i][j]=='m') xm[++man]=i,ym[man]=j;
}
for (int i=1;i<=man;++i)
for (int j=1;j<=house;++j)
a[i][j]=-(abs(xm[i]-xh[j])+abs(ym[i]-yh[j]));
return 1;
}
bool DFS(int k)
{
vx[k]=1;
for (int i=1;i<=house;++i)
if (!vy[i])
{
int temp=lx[k]+ly[i]-a[k][i];
if (!temp)
{
vy[i]=1;
if (!match[i] || DFS(match[i]))
{
match[i]=k;
return 1;
}
}
else
if (slack[i]>temp) slack[i]=temp;
}
return 0;
}
void Solve()
{
memset(match,0,sizeof(match));
memset(lx,200,sizeof(lx));
for (int i=1;i<=man;++i)
for (int j=1;j<=house;++j)
if (lx[i]<a[i][j]) lx[i]=a[i][j];
memset(ly,0,sizeof(ly));
for (int i=1;i<=man;++i)
{
memset(slack,100,sizeof(slack));
while (1)
{
memset(vx,0,sizeof(vx));
memset(vy,0,sizeof(vy));
if (DFS(i)) break;
int Min=INF;
for (int j=1;j<=house;++j)
if (!vy[j] && Min>slack[j]) Min=slack[j];
for (int j=1;j<=man;++j)
if (vx[j]) lx[j]-=Min;
for (int j=1;j<=house;++j)
if (vy[j]) ly[j]+=Min; else slack[j]-=Min;
}
}
int ans=0;
for (int i=1;i<=house;++i)
ans+=a[match[i]][i];
printf("%d\n",-ans);
}
int main()
{
while (Init()) Solve();
return 0;
}