20200311 专题:费用流

总览:

(前置:20191025 练习:网络流(最大流最小割)20191018 专题:网络流(最大流最小割)

全名最小费用最大流

就是在网络流的基础上给每边加上了单位流量的费用,求流量最大时的最小费用

代码也很简单

把网络流中的BFS找深度改成spfa找费用就行

关于spfa:他死了

注意细节(代码里有标注)

T1 P3381【模板】最小费用最大流

P3381【模板】最小费用最大流


题目描述

如题,给出一个网络图,以及其源点和汇点,每条边已知其最大流量和单位流量费用,求出其网络最大流和在最大流情况下的最小费用。


输入格式

第一行包含四个正整数 N 、 M 、 S 、 T N、M、S、T NMST,分别表示点的个数、有向边的个数、源点序号、汇点序号。

接下来M行每行包含四个正整数 u i 、 v i 、 w i 、 f i u_i、v_i、w_i、f_i uiviwifi,表示第i条有向边从 u i u_i ui出发,到达 v i v_i vi,边权为 w i w_i wi(即该边最大流量为 w i w_i wi),单位流量的费用为 f i f_i fi


输出格式

一行,包含两个整数,依次为最大流量和在最大流量情况下的最小费用。


输入输出样例

输入

4 5 4 3
4 2 30 2
4 3 20 3
2 3 20 1
2 1 30 9
1 3 40 5

输出

50 280

说明/提示

时空限制:1000ms,128M

数据规模:

对于100%的数据: N ≤ 5000 , M ≤ 50000 N\le 5000,M\le 50000 N5000M50000


样例说明:

img

如图,最优方案如下:

第一条流为 4 − > 3 4->3 4>3,流量为 20 20 20,费用为 3 ∗ 20 = 60 3*20=60 320=60

第二条流为 4 − > 2 − > 3 4->2->3 4>2>3,流量为 20 20 20,费用为 ( 2 + 1 ) ∗ 20 = 60 (2+1)*20=60 2+120=60

第三条流为 4 − > 2 − > 1 − > 3 4->2->1->3 4>2>1>3,流量为 10 10 10,费用为 ( 2 + 9 + 5 ) ∗ 10 = 160 (2+9+5)*10=160 2+9+510=160

故最大流量为50,在此状况下最小费用为 60 + 60 + 160 = 280 60+60+160=280 60+60+160=280

故输出 50     280 50\ \ \ 280 50   280


思路:

板子


代码:

#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define re register

inline char ch(){
	static char buf[1<<21],*p1=buf,*p2=buf;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;
}

inline int in{
	int s=0,f=1;char x;
	for(x=getchar();x<'0'||x>'9';x=getchar())	if(x=='-')	f=-1;
	for( ;x>='0'&&x<='9';x=getchar())	s=(s<<1)+(s<<3)+(x&15);
	return f==1?s:-s;
}

const int A=1e5+5;
const int INF=1e9;
int n,m,s,t;
int maxflow,mincost;
int head[A],tot_road=1;
struct Road{
	int nex,to,flow,cost;
}road[4*A];
inline void ljb(int x,int y,int z,int c){
	road[++tot_road]={head[x],y,z,c};head[x]=tot_road;
}
int dis[A];
bool ex[A];

inline bool spfa(){
	fill(dis+1,dis+1+n,INF);
	queue <int> q;
	dis[s]=0,ex[s]=1;
	q.push(s);
	while(!q.empty()){
		int x=q.front();q.pop();
		ex[x]=0;
		for(int y=head[x];y;y=road[y].nex){
			int z=road[y].to,w=road[y].flow,c=road[y].cost;
			if(w&&dis[z]>dis[x]+c){
				dis[z]=dis[x]+c;
				if(!ex[z]){
					q.push(z);
					ex[z]=1;
				}
			}
		}
	}
	return dis[t]!=INF;
}

inline int DFS(int x,int flow){
	if(x==t){
		maxflow+=flow;
		return flow;
	}
	int used=0;
	ex[x]=1;
	for(int y=head[x];y;y=road[y].nex){
		int z=road[y].to,w=road[y].flow,c=road[y].cost;
		if(!ex[z]&&w&&dis[z]==dis[x]+c){
			int after=DFS(z,min(w,flow-used));
			if(after){
				used+=after;
				mincost+=after*c;
				road[y].flow-=after;
				road[y^1].flow+=after;
			}
		}
		if(used==flow)	break;
	}
	ex[x]=0;
	return used;
}

inline void MCMF(){
	maxflow=mincost=0;
	while(spfa())	DFS(s,INF);
	return;
}

signed main(){
	n=in,m=in,s=in,t=in;
	while(m--){
		int u=in,v=in,w=in,f=in;
		ljb(u,v,w,f),ljb(v,u,0,-f);
	}
	MCMF();
	printf("%d %d\n",maxflow,mincost);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值