[最小费用最大流算法]运输问题4

最小费用最大流问题就是最每条边都有一个容量的基础上,都再增加了一个费用,在最大流的前提下是费用最小。

基本的思路就是以费用为权建一个图,要特别注意的一点就是当连了map[i][j]=v以后,还需要再建一条map[i][j]=-v的一条反向边,这样我们每次在这张图上做增广就可以了,也就是说我们每次要在这条路上做一遍SPFA,在做SPFA的过程中还要对这条路的容量进行判断,看看是否可行,当不能在增广的时候,就求出了最小费用最大流。

还需要在注意两个问题:①我们每次用SPFA求出了最短路后还需要再乘上流量。②在很多问题中,可能会出现费用不相同的重边,这个时候我们就不能用邻接矩阵来存图了,可以选择用next数组。

13. 运输问题4

【问题描述】
    一个工厂每天生产若干商品,需运输到销售部门进行销售。从产地到销地要经过某些城镇,有不同的路线可以行走,每条两城镇间的公路都有一定的流量限制。公路设有收费站,每通过一辆车,要交纳过路费。请你计算,在不考虑其它车辆使用公路的前提下,如何使产地运输到销地的商品最多,并使费用最少。
【输入格式】
输入文件有若干行
第一行,一个整数n,表示共有n个城市(2<=n<=100),产地是1号城市,销地是n号城市
第二行,一个整数,表示起点城市
第三行,一个整数,表示终点城市
下面有n行,每行有2*n个数字。第p行第2*q-1,2*q列的数字表示城镇p与城镇q之间有无公路连接。数字为0表示无,大于0表示有公路,且这两个数字分别表示该公路流量和每车费用。
【输出格式】
输出文件有一行
第一行,1个整数n,表示最小费用为n。
【输入输出样例】
输入文件名: maxflowd.in
6
1
6
0 0 1 3 5 10 0 0 0 0 0 0 
0 0 0 0 0 0 5 7 0 0 0 0
0 0 0 0 0 0 0 0 2 8 0 0
0 0 0 0 1 3 0 0 0 0 3 5
0 0 2 4 0 0 0 0 0 0 2 6
0 0 0 0 0 0 0 0 0 0 0 0
输出文件名:maxflowd.out
63

邻接矩阵的做法:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int map[101][101][3]={0},pre[101],n,s,tt,l[10000]={0},h,t,dis[101],aa[101]={0};
bool f[101];
int SPFA(int x,int y){
	memset(f,1,sizeof(f));
	memset(dis,127/3,sizeof(dis));
	memset(pre,0,sizeof(pre));
	int i,j,u;
	h=t=1;
	l[h]=x;dis[x]=0;pre[x]=x;
	while(h<=t){
		u=l[h];f[u]=true;
		for(i=1;i<=n;++i){
			if(map[u][i][1]>0&&dis[i]>dis[u]+map[u][i][2]&&i!=u){
				dis[i]=dis[u]+map[u][i][2];
				pre[i]=u;
				if(f[i]){
					t+=1;
					l[t]=i;
					f[i]=false;
				}
			}
		}
		h+=1;
	}
	return dis[y]>10000000?0:dis[y];
}
int ISAP(int x,int y){
	int i,j,minn=1000000000;
	for(i=y;i!=x;i=pre[i]){
		minn=min(minn,map[pre[i]][i][1]);
	}
	for(i=y;i!=x;i=pre[i]){
		map[pre[i]][i][1]-=minn;
		map[i][pre[i]][1]+=minn;
	}
	return minn;
}
int main()
{
	freopen("maxflowd.in","r",stdin);
	freopen("maxflowd.out","w",stdout);
	int i,j,x,y,ans=0;
	scanf("%d%d%d",&n,&s,&tt);
	for(i=1;i<=n;++i){
		for(j=1;j<=n;++j){
			scanf("%d%d",&x,&y);
			map[i][j][1]=x;
			if(y){
			    map[i][j][2]=y;
			    map[j][i][2]=-y;
			}
		}
	}
	x=1;
	while(x){
		x=SPFA(s,tt);
		if(x) ans=ans+x*ISAP(s,tt);
	}	
	printf("%d",ans);
}


next数组做法:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;	
struct S{
	int st,en,va,cost;
}aa[20000];
int point[101]={0},next[20000]={0},pre[101]={0},l[100000]={0},tot=1,n,ss,tt,h,t,dis[101];
bool f[101];
inline void add(int i,int j,int x,int y){
	tot+=1;next[tot]=point[i];point[i]=tot;
	aa[tot].st=i;aa[tot].en=j;aa[tot].va=x;aa[tot].cost=y;
	tot+=1;next[tot]=point[j];point[j]=tot;
	aa[tot].st=j;aa[tot].en=i;aa[tot].va=0;aa[tot].cost=-y;
}
inline int SPFA(int x,int y){
	memset(f,1,sizeof(f));
	memset(dis,127/3,sizeof(dis));
	memset(pre,0,sizeof(pre));
	int i,j,u;
	dis[x]=0;h=t=1;l[h]=x,pre[x]=x;
	while(h<=t){
		u=l[h];
		for(i=point[u];i;i=next[i]){
			if(aa[i].va>0&&dis[aa[i].en]>dis[u]+aa[i].cost&&u!=i){
				dis[aa[i].en]=dis[u]+aa[i].cost;
				pre[aa[i].en]=i;
				if(f[aa[i].en]){
					t+=1;
					f[aa[i].en]=false;
					l[t]=aa[i].en;
				}
			}
		}
		h+=1;
	}
	return dis[y]>1000000?0:dis[y];
} 
inline int ISAP(int x,int y){
	int i,j,minn=n;
	for(i=y;i!=x;i=aa[pre[i]].st){
		minn=min(minn,aa[pre[i]].va);
	}
	for(i=y;i!=x;i=aa[pre[i]].st){
		aa[pre[i]].va-=minn;
		aa[pre[i]^1].va+=minn;
	}
	return minn;
}
int main()
{
	freopen("maxflowd.in","r",stdin);
	freopen("maxflowd.out","w",stdout);
	int i,j,x,y,minn=1,ans=0;
	scanf("%d%d%d",&n,&ss,&tt);
	for(i=1;i<=n;++i){
		for(j=1;j<=n;++j){
			scanf("%d%d",&x,&y);
			if(x) add(i,j,x,y);
		}
	}
	while(minn){
		minn=SPFA(ss,tt);
		if(minn) ans+=minn*ISAP(ss,tt);
	}
	printf("%d\n",ans);
}



  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值