版權聲明:以下大部分內容摘自《算法競賽進階指南》,李煜東著,河南電子音像出版社。
費用流,是網絡流的延伸問題。
每條邊除了有容量限制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;
}