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)\),要求:
- \(0 \leq f(u,v) \leq w(u,v)\)(每条边的流量不超过其流量限制);
- \(\forall p \in \{V \setminus \{s,t\}\}\),\(\sum_{(i,p) \in E}f(i,p)=\sum_{(p,i)\in E}f(p,i)\)(除了源点和汇点外,其他各点流入的流量和流出的流量相等);
- \(\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