题目链接: poj 2195
题目大意: 给出NxM的地图,'.'表示可以走的,'H'表示家,'m'表示人,H和m的数目相同
求把所有人移动到H的最小步数
解题思路: 建立超级源点,分别连接每个m,容量为1,费用0
建立超级汇点,分别把每个H连接到汇点,容量为1,费用为0
再把每个m分别指向H,容量为1,费用为该m到H的横纵左边之差的绝对值的和,|X1-X2|+|Y1-Y2|
PS: 最小费用最大流与一般增广路的区别在于,每次寻找的增广路都是代价最小的路径
代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX 206 #define INF 0x3f3f3f3f #define Min(a,b) ((a<b)?a:b) int S,E,M,H,Index,dist[MAX],pre[MAX],visit[MAX],Map[MAX][MAX],listb[1000000]; int Value[MAX][MAX],path[MAX]; char ch[MAX][MAX]; struct node{ int x,y; }Num1[MAX*MAX],Num2[MAX*MAX]; struct snode{ int c,w,to,next; }Edge[200*200*2]; int Fabs(int x) { return (x<0)?-x:x; } void Add_edge(int a,int b,int c,int d) { Value[a][b]=d; //标记改变的费用 if(c>=0) //构建残留网络 Map[a][b]=c; Edge[Index].to=b,Edge[Index].c=c; Edge[Index].w=d,Edge[Index].next=pre[a]; pre[a]=Index++; } bool BFS() { int i,e,s,v,vv; s=e=0; memset(path,-1,sizeof(path)); memset(dist,INF,sizeof(dist)); memset(visit,0,sizeof(visit)); listb[s++]=S;visit[S]=1; dist[S]=0; while(s!=e) { v=listb[e++]; visit[v]=0; for(i=pre[v];i!=-1;i=Edge[i].next) { vv=Edge[i].to; if(Map[v][vv]>0&&(dist[vv]==INF||dist[vv]>dist[v]+Edge[i].w)) { //最短路寻找代价最小的增广路 path[vv]=v; dist[vv]=dist[v]+Edge[i].w; if(!visit[vv]) //防止重复入队列 { visit[vv]=1; listb[s++]=vv; } } } } if(dist[E]==INF) //不能到达汇点,增广结束 return false; return true; } int Find() { int sum=0,i,MIN=INF; for(i=E;i!=-1;i=path[i]) //找短板 { if(path[i]!=-1) MIN=Min(MIN,Map[path[i]][i]); } for(i=E;i!=-1;i=path[i]) //更新增广路 { if(path[i]!=-1) { Map[path[i]][i]-=MIN; Map[i][path[i]]+=MIN; sum=sum+Value[path[i]][i]; //代价是每条边的权值,所以不需要乘与 } } return sum; } int Solve() { int sum=0; while(BFS()) sum+=Find(); return sum; } int main() { int i,n,j,m; while(scanf("%d%d",&n,&m)!=EOF) { Index=0; if(n==0&&m==0) break; memset(pre,-1,sizeof(pre)); memset(Map,0,sizeof(Map)); memset(Value,0,sizeof(Value)); M=H=0; for(i=1;i<=n;i++) { scanf("%s",ch[i]+1); for(j=1;j<=m;j++) { if(ch[i][j]=='H') { H++; Num1[H].x=i,Num1[H].y=j; } else if(ch[i][j]=='m') { M++; Num2[M].x=i,Num2[M].y=j; } } } int a,b,c,d,i1; S=0,E=H+M+1; for(i=1;i<=M;i++) //左边 { a=S,b=i,c=1,d=0; Add_edge(a,b,c,d); Add_edge(b,a,-c,-d); } for(i=M+1,j=1;j<=H;j++,i++) //右边 { a=i,b=E,c=1,d=0; Add_edge(a,b,c,d); Add_edge(b,a,-c,-d); } for(i=1;i<=M;i++) //中间 { for(j=M+1,i1=1;j<=H+M;j++,i1++) { a=i,b=j,c=1; d=Fabs(Num1[i].x-Num2[i1].x)+Fabs(Num1[i].y-Num2[i1].y); Add_edge(a,b,c,d); Add_edge(b,a,-c,-d); } } printf("%d\n",Solve()); } return 0; }