网络流 最小费用最大流问题

下面给网络里增加一个因素:费用。假设每条边除了有一个容量限制以外,还有一个单位流量所需的费用。在最小费用流问题中,平行边变得有意义:可能会有两条从u到v的弧,费用分别为1和2。在没有费用的情况下,我们可以把二者合并,但由于费用的出现,我们无法合并这两条弧。再如,若边(u,v)和(v,u)均存在,且费用都是负数,则“同时从u流向v和从v流向u”是个很不错的主意。为了方便叙述算法,我们先假定图中不存在平行边和反向边。这样就可以用邻接矩阵cap和cost保存各边的容量和费用。为了便于增广,对于一条边(u,v),我们规定cap[u][v]=0并且cost[v][u]=-cost[u][v],表示沿着(u,v)的相反方向增广时,费用减少cost[u][v]。下面直接给出算法,和EK算法类似,但每次用Bellman-Ford算法,而非BFS找增广路。只要初始流是该流量下最小费用可行流,每次增广后的新流都是新流量下的最小费用流。另外费用值是可正可负的。

queue<int> q;
int d[maxn];
memset(flow, 0, sizeof(flow));
c = f = 0;
while(true){
	//bellman-ford算法开始,在残留网络中找s-t最短路
	bool inq[manx];
	for(int i = 0; i < n; i++) d[i] = (i==s ? 0 : INF);
	memset(inq,0,sizeof(inq));
	q.push(s);
	while(!q.empty)){
		int u = q.front(),q.pop();
		for(int i = 0; i < n; i++){
			if(cap[u][i]>flow[u][i] && d[i]>d[u]+cost[u][i]){
				d[i] = d[u]+cost[u][i];
				p[i] = u;
				if(!inq[i]){
					q.push(i);
					inq[i] = 1;
				}
			}
		}
	} //bellman-ford算法结束
	
	if(d[t]==INF) break; //说明汇点不可达,辨明当前流已经是最小费用最大流
	int a = INF;
	for(int i = t; i!=s; i = p[i]) a = (a>cost[p[i]][i]-flow[p[i]][i] ? cost[p[i]][i]-flow[p[i]][i] : a);
	for(int i = t; i!=s; i = p[i]){
		flow[p[i]][i] += a;
		flow[i][p[i]] -= a;
	}
	c += d[t]*a;
	f += a;
}

运行结束后,f和c分别保存最大流流量和该流量下最小费用。我们要是把邻接矩阵表示方式改成邻接表的表示方式,在此情况下,我们可以去掉“不存在平行边和反向边”的限制(因为邻接表中可以有起点和重点相同的边,而算法不需要经过任何修改, 邻接表的详情见此,这篇文章后半部分,讲述了邻接表的实现思想)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值