网络流之最小费用最大流

1.什么是最小费用最大流?

  打个比方,这是一个运输过程,每条道路连接两座城市,每条路都有限重,也要按照货物数量收取过路费,问最多可以运多少货物。

2.怎么算?

和最大流算法很像,唯一不同的是需要加一个dis数组,然后将bfs改成SPFA即可。

3.注意事项

fa数组保存的是来到这个点的边。

在判断能不能走的时候不需要写fa[to[i]]==0了,在到达终点后也不能break了,因为我们可能要多次修改最小费用。

源点的初始流量为无限大。

4.代码

题目:poj2135

这不能跑两遍最短路,比如下面这组数据:

4 5
1 2 1
2 3 1
3 4 1
1 3 3
2 4 3
答案是8,但是如果你跑两遍最短路的话,第一次找到最短路1->2->3->4后就无法再走到终点了。
所以是求流量为2的网络流最小费用。
由于是无向图,所以每次要加两条正边两条反边。
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<queue>
#include<climits>
using namespace std;
int n,m,sum=1,fy=0,ll=0;
long long ans=0;
int h[1005],to[40005],next[40005],cost[40005];
int fa[1005],que[3005];//fa应该记录的是来的那条路,而不是点,无向图!
long long dis[1005];
bool can[40005],inq[1005];
void add(int x,int y,int z,int ta){
	sum++;to[sum]=y;next[sum]=h[x];h[x]=sum;cost[sum]=z;
	can[sum]=ta;
}
bool bfs(){
	int i,j,head=1,tail=1,from;
	memset(fa,0,sizeof(fa));
	memset(inq,0,sizeof(inq));
	memset(dis,127,sizeof(dis));
	que[1]=1;inq[1]=1;dis[1]=0;
	while(head<=tail){
		from=que[head];
		inq[from]=0;
		for(i=h[from];i!=0;i=next[i]){
			if(can[i]==1&&dis[to[i]]>dis[from]+cost[i]){//不要写fa[to[i]]==0
				dis[to[i]]=dis[from]+cost[i];fa[to[i]]=i;
				if(inq[to[i]]==0){
					tail++;que[tail]=to[i];inq[to[i]]=1;
				}
			}
		}
		head++;
	}
	if(fa[n]==0)return 0;
	return 1;
}
long long find(){
	int i,x,last,cnt=0;
	ll=0;
	while(bfs()){
		ans+=dis[n];
		x=n;
		while(x!=1){
			can[fa[x]]=0;can[fa[x]^1]=1;
			x=to[fa[x]^1];//如果是奇数,减1,如果是偶数,加1
		}
		ll++;
		if(ll==2)break;
	}
	return ans;
}
int main()
{
    int i,j,x,y,z;
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++){
    	scanf("%d%d%d",&x,&y,&z);
    	add(x,y,z,1);add(y,x,-z,0);//1:可走 0:不可走
    	add(y,x,z,1);add(x,y,-z,0);//加两次边
    }
    printf("%lld",find());
    return 0;
}



评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值