传送门
题意:给你一个矩阵,里面有数量相等的H和m,代表房子和人,每个人要找个房子,一个人到一个房子的费用是他走的步数,一个房子只能容纳一个人,求所有人都到房子的最小费用。
个人心得:第一次做最小费用最大流,原本不会费用流的,借着这题就是想学习一下,网上各种博客讲的都好神啊。由于本人最大流只会EK算法(最慢的),所以对于网上大牛的博客各种看不懂,直到看到了这个,里面说就是把EK算法的BFS过程改成SPFA,于是顿悟,好吧,就是这点差别。我理解了下她的代码,然后完成了人生第一道费用流。不过目前掌握的算法效率不高,有待学习新的算法。
思路:感觉没啥好说的,主要就是建图,添加超级源点和汇点,然后就是按照EK+SPFA的思路做了。
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #include<cmath> #define maxn 2000 using namespace std; int m,n; int hnum,mnum,hx[200],hy[200],mx[200],my[200]; int c[210][210],cost[210][210],f[210][210],d[210],pre[210]; bool v[210]; char str[200]; int abs(int x) { return x>0?x:-x; } int EK(int s,int t) { int ans=0; memset(f,0,sizeof(f)); memset(v,0,sizeof(v)); queue<int>q; while(1) { for(int i=0;i<=t;i++)d[i]=maxn; d[s]=0; q.push(s); v[s]=1; while(!q.empty()) { int a=q.front(); q.pop(); v[a]=0; for(int i=s;i<=t;i++) { if(c[a][i]-f[a][i]>0&&d[i]>d[a]+cost[a][i]) { pre[i]=a; d[i]=d[a]+cost[a][i]; if(!v[i]) { q.push(i); v[i]=1; } } } } if(d[t]==maxn)break; int mi=maxn; for(int i=t;i!=s;i=pre[i]) { if(c[pre[i]][i]-f[pre[i]][i]<mi)mi=c[pre[i]][i]-f[pre[i]][i]; } for(int i=t;i!=s;i=pre[i]) { f[pre[i]][i]+=mi; f[i][pre[i]]-=mi; ans+=cost[pre[i]][i]; } } return ans; } int main() { while(scanf("%d%d",&m,&n)&&n&&m) { hnum=mnum=0; memset(c,0,sizeof(c)); memset(cost,0,sizeof(cost)); for(int i=0;i<m;i++) { scanf("%s",str); for(int j=0;j<n;j++) { if(str[j]=='H') { hx[++hnum]=i; hy[hnum]=j; } else if(str[j]=='m') { mx[++mnum]=i; my[mnum]=j; } } } for(int i=1;i<=mnum;i++) { c[0][i]=1; for(int j=1;j<=hnum;j++) { int hh=mnum+j; c[i][hh]=1; cost[i][hh]=abs(mx[i]-hx[j])+abs(my[i]-hy[j]); cost[hh][i]=-cost[i][hh]; } } for(int i=1;i<=hnum;i++) { c[i+mnum][hnum+mnum+1]=1; } printf("%d\n",EK(0,hnum+mnum+1)); } return 0; }