其他分享
首页 > 其他分享> > P3381【模板】最小费用最大流

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

作者:互联网

【模板】最小费用最大流

题目描述

给出一个包含 \(n\) 个点和 \(m\) 条边的有向图(下面称其为网络) \(G=(V,E)\),该网络上所有点分别编号为 \(1 \sim n\),所有边分别编号为 \(1\sim m\),其中该网络的源点为 \(s\),汇点为 \(t\),网络上的每条边 \((u,v)\) 都有一个流量限制 \(w(u,v)\) 和单位流量的费用 \(c(u,v)\)。

你需要给每条边 \((u,v)\) 确定一个流量 \(f(u,v)\),要求:

  1. \(0 \leq f(u,v) \leq w(u,v)\)(每条边的流量不超过其流量限制);
  2. \(\forall p \in \{V \setminus \{s,t\}\}\),\(\sum_{(i,p) \in E}f(i,p)=\sum_{(p,i)\in E}f(p,i)\)(除了源点和汇点外,其他各点流入的流量和流出的流量相等);
  3. \(\sum_{(s,i)\in E}f(s,i)=\sum_{(i,t)\in E}f(i,t)\)(源点流出的流量等于汇点流入的流量)。

定义网络 \(G\) 的流量 \(F(G)=\sum_{(s,i)\in E}f(s,i)\),网络 \(G\) 的费用 \(C(G)=\sum_{(i,j)\in E} f(i,j) \times c(i,j)\)。

你需要求出该网络的最小费用最大流,即在 \(F(G)\) 最大的前提下,使 \(C(G)\) 最小。

输入格式

输入第一行包含四个整数 \(n,m,s,t\),分别代表该网络的点数 \(n\),网络的边数 \(m\),源点编号 \(s\),汇点编号 \(t\)。

接下来 \(m\) 行,每行四个整数 \(u_i,v_i,w_i,c_i\),分别代表第 \(i\) 条边的起点,终点,流量限制,单位流量费用。

输出格式

输出两个整数,分别为该网络的最大流 \(F(G)\),以及在 \(F(G)\) 最大的前提下,该网络的最小费用 \(C(G)\)。

样例 #1

样例输入 #1

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

样例输出 #1

50 280

提示

对于 \(100\%\) 的数据,\(1 \leq n \leq 5\times 10^3\),\(1 \leq m \leq 5 \times 10^4\),\(1 \leq s,t \leq n\),\(u_i \neq v_i\),\(0 \leq w_i,c_i \leq 10^3\),且该网络的最大流和最小费用 \(\leq 2^{31}-1\)。

输入数据随机生成。

PS

求解最大费用最小流只需要在原有基础上稍微修改,把边的费用设为原来的负数,求解最小费用最大流,最后输出相反数即为最大费用最大流,可以看网络流24题中的 运输问题,是一道费用流的裸题,需要求出最小费用和最大费用。

代码

// Problem: P3381 【模板】最小费用最大流
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3381
// Memory Limit: 128 MB
// Time Limit: 1000 ms
// Time: 2022-07-27 13:55:41
// 
// Powered by CP Editor (https://cpeditor.org)

//fw
#include<iostream>
#include<cstdio>
#include<fstream>
#include<algorithm>
#include<cmath>
#include<deque>
#include<vector>
#include<queue>
#include<string>
#include<cstring>
#include<map>
#include<stack>
#include<set>
#include<climits>
#define zp ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);
#define pii pair <int, int>
#define endl '\n'
#define pb push_back
#define lc u<<1
#define rc u<<1|1
using namespace std;
typedef long long ll;
const int N = 5010, M = 100010, INF = 1e8;
int n,m,S,T;
int h[N],e[M],ne[M],w[M],f[M],idx;//前向星、费用、容量
int q[N],d[N],pre[N],incf[N];//spfa队列、d[i]:到i点的最小费用、pre[i]经过哪条边到i点,incf[i],到i点的最大可行流流量
bool st[N];//spfa状态数组
void add(int a,int b,int c,int d)
{
	//与最大流中的退流类似,费用流的w数组反向边应设为正向边的相反数,意为“退费”
	e[idx]=b,f[idx]=c,w[idx]=d,ne[idx]=h[a],h[a]=idx++;
	e[idx]=a,f[idx]=0,w[idx]=-d,ne[idx]=h[b],h[b]=idx++;
	
}
bool spfa()
{
	
	//初始化
	int hh=0,tt=1;
	memset(d,0x3f,sizeof d);
	memset(incf,0,sizeof incf);
	q[0]=S,d[S]=0,incf[S]=INF;
	
	//寻找费用最短路
	while(hh!=tt)
	{
		int t=q[hh++];
		if(hh==N)hh=0;//循环队列
		st[t]=false;

		for(int i=h[t];~i;i=ne[i])
		{
			int ver=e[i];
			if(f[i]&&d[ver]>d[t]+w[i])//如果仍有流量可以流并且费用更小
			{
				//更新最小费用
				d[ver]=d[t]+w[i];
				//更新前驱边
				pre[ver]=i;
				//更新流量限制
				incf[ver]=min(f[i],incf[t]);
				//入队
				if(!st[ver])
				{
					q[tt++]=ver;
					if(tt==N)tt=0;
					st[ver]=true;
				}
			}
		}
	}
	return incf[T]>0;
}
void EK(int &flow,int &cost)//利用传引用修改
{	
	//初始化流量和费用
	flow=cost=0;
	//如果能找到增广路
	while(spfa())
	{
		//取出到达汇点的最大流量
		int t=incf[T];
		//累加流量和费用
		flow+=t,cost+=t*d[T];
		//扣除增广路上的边上的流量
		for(int i=T;i!=S;i=e[pre[i]^1])
		{
			f[pre[i]]-=t;
			f[pre[i]^1]+=t;
		}
	}
}
int main()
{
   scanf("%d %d %d %d",&n,&m,&S,&T);
    memset(h,-1,sizeof h);//初始化
    //加边
    while(m--)
    {
    	int a,b,c,d;
    	scanf("%d %d %d %d",&a,&b,&c,&d);
    	add(a,b,c,d);
    	
    }	
    //求解
    int flow,cost;
    EK(flow,cost);
    //输出
  printf("%d %d\n",flow,cost);
    return 0;
}

标签:费用,int,最小,流量,网络,leq,P3381,include,模板
来源: https://www.cnblogs.com/avarice/p/16581232.html