费用流算法小结

3 篇文章 0 订阅
2 篇文章 0 订阅

版权声明:以下大部分内容摘自《算法竞赛进阶指南》,李煜东著,河南电子音像出版社。

费用流,是网络流的延伸问题。
每条边除了有容量限制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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值