2174. 费用流

题目链接

2174. 费用流

给定一个包含 \(n\) 个点 \(m\) 条边的有向图,并给定每条边的容量和费用,边的容量非负。

图中可能存在重边和自环,保证费用不会存在负环。

求从 \(S\)\(T\) 的最大流,以及在流量最大时的最小费用。

输入格式

第一行包含四个整数 \(n,m,S,T\)

接下来 \(m\) 行,每行三个整数 \(u,v,c,w\),表示从点 \(u\) 到点 \(v\) 存在一条有向边,容量为 \(c\),费用为 \(w\)

点的编号从 \(1\)\(n\)

输出格式

输出点 \(S\) 到点 \(T\) 的最大流和流量最大时的最小费用。

如果从点 \(S\) 无法到达点 \(T\) 则输出 0 0

数据范围

\(2≤n≤5000\),
\(1≤m≤50000\),
\(0≤c≤100\),
\(-100 \le w \le 100\)
\(S≠T\)

输入样例:

5 5 1 5
1 4 10 5
4 5 5 10
4 2 12 5
2 5 10 15
1 5 10 10

输出样例:

20 300

解题思路

费用流

引入费用流时流网络中的边不止有流量的概念,还得有费用的概念
费用流即最小/大费用最大流(即最大可行流中的最小/大费用)的简称
费用流:\(最大流时每条边上的可行流\times 该边上的费用\)
EK 算法改进的费用流算法比较常见,这里主要讨论这个改进后的算法
主要是将 EK 算法中的 bfs 改为 spfaspfa 用来求解源点 \(s\)\(t\) 的最短/长路(即 \(s\)\(t\) 上的最小路径费用和)在求最短/长路的同时找到一条增广路径,然后增加这部分的费用,直到找不到增广路径为止,\(\color{red}{为什么这样可以求解费用流}\),假设对于当前可行流 \(f_1\)\(f_1\) 是费用最小的,假设在 \(f_1\) 的残余网络中经过 spfa 找到一条增广路径,即可行流 \(f_2\),则可知 \(f=f_1+f_2\) 仍是一个可行流,假设此时 \(f\) 虽然是当前为止费用并非最小的可行流,即有这样一个可行流 \(f'=f_1+f_2'\),其中 \(|f|=|f'|\),但是 \(f'\) 的费用要比 \(f\) 小,注意,此时有 \(|f_2|=|f_2'|\),设 \(cost(f)\)\(f\) 这个可行流的费用,则 \(cost(f')=cost(f_1)+cost(f_2')=cost(f_1)+|f_2'|\times dist(f_2'))<cost(f)=cost(f_1)+cost(f_2)=cost(f_1)+|f_2|\times dist(f_2)\),则有 \(dist(f_2')<dist(f_2)\),由于 \(dist(f_2)\) 已经是最短路了,故假设不成立,故这种方法正确

设最大流为 \(f\)\(k\)spfa 算法的常数,则:

  • 时间复杂度:\(O(kmf)\)

类似的,也有 dinic 算法的改进版

  • 时间复杂度:\(O(kmf)\)

代码

  • EK 算法改进
// Problem: 费用流
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/2176/
// Memory Limit: 64 MB
// Time Limit: 5000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=5005,M=100005,inf=1e9;
int n,m,S,T;
int h[N],ne[M],f[M],w[M],e[M],idx;
int incf[N],d[N],q[N],pre[N];
bool st[N];
void add(int a,int b,int c,int d)
{
	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 x=q[hh++];
		if(hh==N)hh=0;
		st[x]=false;
		for(int i=h[x];~i;i=ne[i])
		{
			int y=e[i];
			if(d[y]>d[x]+w[i]&&f[i])
			{
				d[y]=d[x]+w[i];
				pre[y]=i;
				incf[y]=min(incf[x],f[i]);
				if(!st[y])
				{
					q[tt++]=y;
					if(tt==N)tt=0;
					st[y]=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()
{
	memset(h,-1,sizeof h);
    scanf("%d%d%d%d",&n,&m,&S,&T);
    for(int i=1;i<=m;i++)
    {
    	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",flow,cost);
    return 0;
}
  • dinic 算法改进
// Problem: 费用流
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/2176/
// Memory Limit: 64 MB
// Time Limit: 5000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=5005,M=100005,inf=1e9;
int n,m,S,T;
int h[N],ne[M],f[M],w[M],e[M],idx;
int incf[N],d[N],q[N],cur[N];
bool st[N];
void add(int a,int b,int c,int d)
{
	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()
{
	for(int i=1;i<=n;i++)cur[i]=h[i],d[i]=inf,incf[i]=0;
	d[S]=0,incf[S]=inf;
	q[0]=S;
	int hh=0,tt=1;
	while(hh!=tt)
	{
		int x=q[hh++];
		if(hh==N)hh=0;
		st[x]=false;
		for(int i=h[x];~i;i=ne[i])
		{
			int y=e[i];
			if(d[y]>d[x]+w[i]&&f[i])
			{
				d[y]=d[x]+w[i];
				incf[y]=min(incf[x],f[i]);
				if(!st[y])
				{
					q[tt++]=y;
					if(tt==N)tt=0;
					st[y]=true;
				}
			}
		}
	}
	return incf[T]>0;
}
int dfs(int x,int limit,int &cost)
{
	if(x==T)
	{
		cost+=d[T]*limit;
		return limit;
	}
	st[x]=true;
	int flow=0;
	for(int i=cur[x];~i&&flow<limit;i=ne[i])
	{
		int y=e[i];
		if(d[y]==d[x]+w[i]&&f[i]&&!st[y])
		{
			int t=dfs(y,min(f[i],limit-flow),cost);
			if(!t)d[y]=inf;
			f[i]-=t,f[i^1]+=t,flow+=t;
		}
	}
	st[x]=false;
	return flow;
}
void dinic(int &max_flow,int &cost)
{
	max_flow=cost=0;
	int flow=0;
	while(spfa())while(flow=dfs(S,inf,cost))max_flow+=flow;
}
int main()
{
	memset(h,-1,sizeof h);
    scanf("%d%d%d%d",&n,&m,&S,&T);
    for(int i=1;i<=m;i++)
    {
    	int a,b,c,d;
    	scanf("%d%d%d%d",&a,&b,&c,&d);
    	add(a,b,c,d);
    }
    int flow,cost;
    dinic(flow,cost);
    printf("%d %d",flow,cost);
    return 0;
}
posted @ 2022-12-01 20:54  zyy2001  阅读(63)  评论(0编辑  收藏  举报