题意:
给出一个n*m的图,其中m是人,H是房子,每个人移动一步需要一块,问所有人移动到房子里的最少花费。
建图:
建立一个超级源点和超级汇点,S=0,T=2*num+1。
从源点到所有的人建立一条流,容量cap[S][i]=1,费用cost[S][i]=0;
从房子到汇点建立一条流,容量cap[j][T]=1,费用cost[j][T]=0;
建立所有人到所有房子的流,cap[i][j]=1,cost[i][j]为两个坐标的差值。
然后求一次费用流即可。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <cmath>
#include <cstring>
#include <queue>
#include <set>
#include <vector>
#include <stack>
#include <map>
#include <iomanip>
#define PI acos(-1.0)
#define Max 5005
#define inf 1<<28
#define LL(x) (x<<1)
#define RR(x) (x<<1|1)
using namespace std;
struct kdq
{
int x,y;
}human[Max],house[Max];
int n,m;
int S,T;//源点,汇点
int cost[Max/10][Max];//花费
int cap[Max/10][Max];//容量
int dis[Max];
int path[Max];
bool visit[Max];
int q[Max*10];
int spfa()//最短路
{
int i,j;
for(i=0;i<=T;i++)
dis[i]=inf,path[i]=-1,visit[i]=0;
dis[S]=0;
visit[S]=1;
int num=0,cnt=0;
q[num++]=S;
while(num>cnt)
{
int temp=q[cnt++];
visit[temp]=0;
for(i=0;i<=T;i++)
{
if(cap[temp][i]&&dis[temp]+cost[temp][i]<dis[i])
{
path[i]=temp;
dis[i]=dis[temp]+cost[temp][i];
if(!visit[i])
{
q[num++]=i;
visit[i]=1;
}
}
}
}
return dis[T]!=inf;
}
int minCost=0;
void getMaxFlow()//增广找最大流
{
int maxFlow=inf;
while(spfa())
{
int pre=T;
while(path[pre]!=-1)
{
maxFlow=min(maxFlow,cap[path[pre]][pre]);
pre=path[pre];
}
pre=T;
minCost+=dis[T]*maxFlow;//最小费用
while(path[pre]!=-1)//更新流
{
cap[path[pre]][pre]-=maxFlow;
cap[pre][path[pre]]+=maxFlow;
//minCost+=cost[path[pre]][pre]*maxFlow;
pre=path[pre];
}
}
cout<<minCost<<endl;
return ;
}
int getdis(kdq x,kdq y)//两个坐标之间的费用
{
return (abs(x.x-y.x)+abs(y.y-x.y));
}
void build_map(int numm,int numh)//建图
{
int i,j;
for(i=1;i<=numm;i++)//计算房子和人之间的费用
for(j=1;j<=numh;j++){
cost[i][j+numm]=getdis(human[i],house[j]);
cost[j+numm][i]=-cost[i][j+numm];//负费用用来回流
}
for(i=1;i<=numm;i++)//源点到每个人的cap,cost
cap[S][i]=1,cost[S][i]=0;
for(i=1;i<=numh;i++)//房子到汇点的cap,cost
cap[i+numm][T]=1;
for(i=1;i<=numm;i++)//每个人和房子之间的cap
for(j=1;j<=numh;j++)
cap[i][j+numm]=1;
}
int main()
{
int i,j,k,l;
char x;
while(scanf("%d%d",&n,&m),(n+m))
{
memset(cap,0,sizeof(cap));
memset(cost,0,sizeof(cost));
int numm=0,numh=0;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
{
cin>>x;
if(x=='m'){
human[++numm].x=i;
human[numm].y=j;
}
if(x=='H'){
house[++numh].x=i;
house[numh].y=j;
}
}
S=0;
minCost=0;
T=numm+numh+1;//其实numm==numh。。。。
build_map(numm,numh);
getMaxFlow();
}
return 0;
}
(转)KM算法,同样可以解此题。留下来看看
这是一个典型的最大匹配的题目,题目意思是给出一些房子和一些人,每个人到每个房子都有一个相应的代价,最后要求怎么安排这些人,房子和人一一配对,使最后的代价最小。
方法是KM算法,是一个求最大(最小)匹配的一个很强大的算法。不过这种题目还可以用费用流来做。
下面是某牛的对KM算法讲解
http://hi.baidu.com/anonympine/blog/item/3ee64954fe6f6256574e0021.html
KM算法是通过给每个顶点一个标号(叫做顶标)来把求最大权匹配的问题转化为求完备匹配的问题的。设顶点Xi的顶标为A[i],顶点Yi的顶标为B[i],顶点Xi与Yj之间的边权为w[i,j]。在算法执行过程中的任一时刻,对于任一条边(i,j),A[i]+B[j]>=w[i,j]始终成立。KM算法的正确性基于以下定理:
若由二分图中所有满足A[i]+B[j]=w[i,j]的边(i,j)构成的子图(称做相等子图)有完备匹配,那么这个完备匹配就是二分图的最大权匹配。
这个定理是显然的。因为对于二分图的任意一个匹配,如果它包含于相等子图,那么它的边权和等于所有顶点的顶标和;如果它有的边不包含于相等子图,那么它的边权和小于所有顶点的顶标和。所以相等子图的完备匹配一定是二分图的最大权匹配。
初始时为了使A[i]+B[j]>=w[i,j]恒成立,令A[i]为所有与顶点Xi关联的边的最大权,B[j]=0。如果当前的相等子图没有完备匹配,就按下面的方法修改顶标以使扩大相等子图,直到相等子图具有完备匹配为止。
我们求当前相等子图的完备匹配失败了,是因为对于某个X顶点,我们找不到一条从它出发的交错路。这时我们获得了一棵交错树,它的叶子结点全部是X顶点。现在我们把交错树中X顶点的顶标全都减小某个值d,Y顶点的顶标全都增加同一个值d,那么我们会发现:
- 两端都在交错树中的边(i,j),A[i]+B[j]的值没有变化。也就是说,它原来属于相等子图,现在仍属于相等子图。
- 两端都不在交错树中的边(i,j),A[i]和B[j]都没有变化。也就是说,它原来属于(或不属于)相等子图,现在仍属于(或不属于)相等子图。
- X端不在交错树中,Y端在交错树中的边(i,j),它的A[i]+B[j]的值有所增大。它原来不属于相等子图,现在仍不属于相等子图。
- X端在交错树中,Y端不在交错树中的边(i,j),它的A[i]+B[j]的值有所减小。也就说,它原来不属于相等子图,现在可能进入了相等子图,因而使相等子图得到了扩大。
以上就是KM算法的基本思路。但是朴素的实现方法,时间复杂度为O(n4)——需要找O(n)次增广路,每次增广最多需要修改O(n)次顶标,每次修改顶标时由于要枚举边来求d值,复杂度为O(n2)。实际上KM算法的复杂度是可以做到O(n3) 的。我们给每个Y顶点一个“松弛量”函数slack,每次开始找增广路时初始化为无穷大。在寻找增广路的过程中,检查边(i,j)时,如果它不在相等子图中,则让slack[j]变成原值与A[i]+B[j]-w[i,j]的较小值。这样,在修改顶标时,取所有不在交错树中的Y顶点的slack值中的最小值作为d值即可。但还要注意一点:修改顶标后,要把所有的slack值都减去d。
不是特别理解这个算法,但是代码是照着大牛的写出来了.以后再研究吧!!!
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <cmath>
#include <cstring>
#include <queue>
#include <set>
#include <vector>
#include <stack>
#include <map>
#include <iomanip>
#define PI acos(-1.0)
#define Max 2005
#define inf 1<<28
#define LL(x) (x<<1)
#define RR(x) (x<<1|1)
using namespace std;
int n,m;
struct kdq
{
int x,y;
void k(int xx,int yy)
{
x=xx,y=yy;
}
int diss(const kdq &aa)
{
return abs(x-aa.x)+abs(y-aa.y);
}
};
kdq a[Max],b[Max];
int x[Max],y[Max];
int match[Max];
int dis[Max][Max];
int km1[Max],km2[Max];
int _man,_house;
int d;
int dfs(int cur)
{
x[cur]=1;
for(int i=0; i<_man; i++)
{
if(!y[i])
{
int t=km1[cur]+km2[i]-dis[cur][i];
t=-t;
if(!t)
{
y[i]=1;
if(match[i]==-1||dfs(match[i]))
{
match[i]=cur;
return 1;
}
}
else if(d>t)
d=t;
}
}
return 0;
}
void km()
{
for(int i=0; i<_man; i++)
{
while(1)
{
memset(x,0,sizeof(x));
memset(y,0,sizeof(y));
d=inf;
if(dfs(i))
break;
for(int j=0; j<_man; j++)
{
if(x[j])km1[j]+=d;//最大权做相反处理
if(y[j])km2[j]-=d;
}
}
}
}
int main()
{
int i,j,k,l;
while(scanf("%d%d",&n,&m),(n+m))
{
char xx;
_man=_house=0;
memset(match,-1,sizeof(match));
memset(km2,0,sizeof(km2));
for(i=0; i<=n; i++)
km1[i]=inf;//最小权,最大权则赋值成0;
for(i=1; i<=n; i++)
{
for(j=1; j<=m; j++)
{
cin>>xx;
if(xx=='m')
{
a[_man++].k(i,j);
}
if(xx=='H')
{
b[_house++].k(i,j);
}
}
}
for(i=0; i<_man; i++)
{
for(j=0; j<_house; j++)
{
dis[i][j]=a[i].diss(b[j]);
if(km1[i]>dis[i][j])
km1[i]=dis[i][j];
}
}
km();
int ans=0;
for(i=0; i<_man; i++)
ans+=(km1[i]+km2[i]);
cout<<ans<<endl;
}
return 0;
}