题意:给定一个地图(n*m),地图上有若干个man和house(<=100),且man与house的数量一致。man移动一格需花费1费用(即单位费用=单位距离),一间house只能入住一个man。现在要求所有的man都入住house,求最小费用。
最小费用最大流的模板题。
(但是也可以看做是求二分图的带权匹配问题,用KM算法解决,这里先贴最小费用最大流的代码)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define INF 0x7fffffff
using namespace std;
struct point
{
int x;
int y;
};
point p[500];
int n;
int val[300][300],pre[300],dist[300],dis[300][300];
char map[300][300];
int spfa()
{
int u,v,t2;
bool inque[300];
queue<int> q;
while (!q.empty())
q.pop();
memset(dist,-1,sizeof(dist));
memset(inque,false, sizeof(inque));
dist[1]=0;
inque[1]=true;
q.push(1);
while (!q.empty())
{
u=q.front();
q.pop();
inque[u]=false;
for (v=1; v<=n; v++)
{
if (val[u][v] != 0 && (dist[v] == -1 || dist[v] > dist[u]+dis[u][v]))
{
dist[v]=dist[u]+dis[u][v];
pre[v]=u;
if (inque[v] == false)
{
inque[v]=true;
q.push(v);
}
}
}
}
return dist[n];
}
int abs(int a)
{
return a>0?a:-a;
}
int min(int a,int b)
{
return a>b?b:a;
}
int Distance(point a,point b)
{
return abs(a.x-b.x)+abs(a.y-b.y);
}
int main()
{
int N,M,i,j,cnt,t1,t2,ans;
int flow;
char tc;
while (1)
{
scanf("%d%d",&N,&M);
if (N == 0 && M == 0)
break;
getchar();
cnt=0;
for (i=0; i<N; i++)
{
scanf("%s",map[i]);
for (j=0; j<M; j++)
{
if (map[i][j] == 'H')
cnt++;
}
}
t1=2;
t2=cnt+2;
for (i=0; i<N; i++)
{
for (j=0; j<M; j++)
{
if (map[i][j] == 'H')
{
p[t1].x=i;
p[t1].y=j;
t1++;
}
if (map[i][j] == 'm')
{
p[t2].x=i;
p[t2].y=j;
t2++;
}
}
}
n=t2;
memset(val,0,sizeof(val));
memset(dis,0,sizeof(dis));
for (i=2; i<t1; i++)
{
val[1][i]=1;
}
for (i=2; i<t1; i++)
{
for (j=cnt+2; j<t2; j++)
{
val[i][j]=1;
dis[i][j]=Distance(p[i],p[j]);
dis[j][i]=-dis[i][j];
}
}
for (j=cnt+2; j<t2; j++)
{
val[j][n]=1;
}
ans=0;
while (spfa() != -1)
{
flow=INF;
for (i=n; i != 1; i=pre[i])
{
flow=min(flow,val[pre[i]][i]);
}
for (i=n; i != 1; i=pre[i])
{
val[pre[i]][i]-=flow;
val[i][pre[i]]+=flow;
ans+=flow*dis[pre[i]][i];
}
}
printf("%d\n",ans);
}
return 0;
}