版权声明:以下大部分内容摘自《算法竞赛进阶指南》,李煜东著,河南电子音像出版社。
费用流,是网络流的延伸问题。
每条边除了有容量限制L,还有单位费用W。
每从这条边流出1个单位流量,就花费W的费用。
如果是“最小费用最大流”,那就是最短路;
如果是“最大费用最大流”,那就是最长路。
费用流算法是将EK算法中的BFS改成SPFA(或Dijkstra),将W当成边权即可。
对于反向边,容量仍为0,费用为正向边费用的相反数。
相当于一个司机运货,走错了路要将花费的费用退还,再走别的路。(现实生活中就算了吧)
例题也摘自《算法竞赛进阶指南》的“K取方格数”
不同的费用流有不同的构造方式,具体见题目要求,当然,注意W是单位费用,与流量有关,所以不要瞎用费用流打题,思维不要僵化于费用流中,W也必须谨慎使用,图要谨慎构造,确保其正确性。
粘贴个代码(“K取方格数”,过掉了洛谷与POJ的数据)
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
int f[200005][5],q[10005];
int dist[10005],flow[10005],spfa[10005],pre[10005],last[10005];
int i,j,k,m,n,o,p,l,s,t,S,T,times,c,ans;
void insert(int x,int y,int z,int w) {
f[++t][1]=y,f[t][2]=q[x],f[t][3]=z,f[t][4]=w,q[x]=t;
f[++t][1]=x,f[t][2]=q[y],f[t][3]=0,f[t][4]=-w,q[y]=t;
}
int num(int x,int y,int z) {return x*n*n+(y-1)*n+z;}
int costflow()
{
queue<int>h;
memset(dist,-1,sizeof(dist));memset(spfa,0,sizeof(spfa));
h.push(S);flow[S]=1e9;dist[S]=0;spfa[S]=1;
while (!h.empty())
{
int st=h.front();h.pop();
for (int k=q[st];k;k=f[k][2])
{
if (!f[k][3]) continue;
if (dist[st]+f[k][4]>dist[f[k][1]])
{
dist[f[k][1]]=dist[st]+f[k][4];
flow[f[k][1]]=min(flow[st],f[k][3]);pre[f[k][1]]=k;
if (!spfa[f[k][1]]) h.push(f[k][1]),spfa[f[k][1]]=1;
}
}spfa[st]=0;
}
return dist[T]!=-1;
}
void update()
{
ans+=dist[T]*flow[T];
int k=T;
while (k!=S)
{
f[pre[k]][3]-=flow[T];
f[pre[k]^1][3]+=flow[T];
k=f[pre[k]^1][1];
}
}
int read(int &x)
{
char ch=getchar();x=0;
while (ch<'0'||ch>'9') ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-48,ch=getchar();
}
int main()
{
read(n),read(times);t=1;
for (i=1;i<=n;i++)
for (j=1;j<=n;j++)
{
read(c);
insert(num(0,i,j),num(1,i,j),1,c);
insert(num(0,i,j),num(1,i,j),times-1,0);
if (i<n) insert(num(1,i,j),num(0,i+1,j),times,0);
if (j<n) insert(num(1,i,j),num(0,i,j+1),times,0);
}
S=1,T=2*n*n;
while (costflow())
update();
printf("%d\n",ans);
return 0;
}