費用流算法小結

版權聲明:以下大部分內容摘自《算法競賽進階指南》,李煜東著,河南電子音像出版社。

費用流,是網絡流的延伸問題。

每條邊除了有容量限制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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章