注 意 \color{Red}注意

无源汇上下界可行流

⚪ \color{Red}{⚪} 问题描述

一个有向图,每条边拥有一个流量下界 L L L和流量上界 R R R

是否存在一种方式使得在满足流量守恒的情况下,满足每条边的上下界。


因为每条边至少需要 L L L的流量,那初始时,直接给每条边输入 L L L的流量。

但是这样,肯定是不满足流量守恒的。

i n x in_x inx表示流入 x x x的流量, o u t i out_i outi表示流出 x x x的流量

对于一个点 i n x > o u t x in_x>out_x inx>outx,说明流出的流量少了

对于一个点 i n x < o u t x in_x<out_x inx<outx,说明流入的流量少了

为了满足流量守恒,需要给一些边加上一些流量,但是选择哪些边呢??

考虑虚拟源点 s s ss ss和汇点 t t tt tt

i n x > o u t x in_x>out_x inx>outx,由 s s ss ss连向 x x x一条流量 i n x − o u t x in_x-out_x inxoutx的边

含义是,因为流入 x x x的流量多出了 i n x − o u t x in_x-out_x inxoutx,需要为这些多出来的流找个去处

那么就让 s s ss ss输入给 x x x这么多流量,意味着 x x x作为出点的那些边从 x x x这里多拿走 i n x − o u t x in_x-out_x inxoutx的流量

这些从 x x x流出去,流给哪些边分别流多少不关心,只关心最后是不是所有的 i n x − o u t x in_x-out_x inxoutx都流走了

i n x < o u t x in_x<out_x inx<outx,由 x x x连向 t t tt tt一条流量 o u t x − i n x out_x-in_x outxinx的边

含义是,因为流出 x x x的流量多了 o u t x − i n x out_x-in_x outxinx,那这部分流量哪里来的呢?

那么就让 x x x输入 t t tt tt这么多流量,意味着在 x x x作为入点的那些边需要多输入 o u t x − i n x out_x-in_x outxinx的流量

哪些边流给 x x x分别流多少不关心,只关心最后总共是否流入了 o u t x − i n x out_x-in_x outxinx x x x,那么 x x x再流向 t t t

这样从源点 s s ss ss跑最大流,若所有 s s ss ss的出边满流说明有解,否则无解。

有解时,每条边的流量是流量下限加上这条边的附加流

代码LINK

有源汇上下界可行流

⚪ \color{Red}{⚪} 问题描述

n n n个点 m m m条边,每条边流量限制是 [ L , R ] [L,R] [L,R]

求满足流量守恒和满足边流量限制的情况下,求源点 s s s到汇点 t t t的可行流


和前面相比,源点 s s s只输出流量,汇点 t t t只接受流量,这两个点不需要流量守恒

但是我们发现!!源点流出的流量必定等于流入汇点的流量

这是一个非常好的性质,

此时只需要让 t t t连向 s s s一条流量上下界为 [ 0 , i n f ] [0,inf] [0,inf]的边即可. s s s流向 t t t, t t t再还给 s s s.

这样,即使是 s s s t t t也满足流量守恒了,就可以用上面求无源汇可行流的方法

有源汇上下界最大流

⚪ \color{Red}{⚪} 问题描述

n n n个点 m m m条边,每条边流量限制是 [ L , R ] [L,R] [L,R]

求满足流量守恒和满足边流量限制的情况下,求源点 s s s到汇点 t t t的最大流


显然可以先套用有源汇上下界可行流求出一个可行流 f 0 f_0 f0

现在问,为什么 f 0 f_0 f0可能不是最大流??

我们构造的过程, s s ss ss连出去的所有边流量之和是:流入的[下界和]减去流出的[下界和].

那么这些点实际流出的流量是流入的[下界和],流入的上界可能还远远没有达到

这时,我们从 s s s t t t跑网络流增广即可扩大流量。

而且因为 s s ss ss t t tt tt的所有边已经满流了,网络流不会走满流的边,所以不会破坏流量守恒。

具体做法(两种做法)

①.跑无源汇可行流得到可行流 f 0 f_0 f0,删掉 t t t连向 s s s [ 0 , i n f ] [0,inf] [0,inf]边,由 s s s t t t跑网络流得到 f 1 f_1 f1

答案是 f 0 + f 1 f_0+f_1 f0+f1

②.跑无源汇可行流判断解,然后求 s s s t t t的最大流即为答案。

为什么?? 先定义 e 1 e_1 e1 t t t连向 s s s的正向弧, e 2 e_2 e2 s s s t t t,是 e 1 e_1 e1的反向弧

那么 s s s t t t增广,不可能走 e 1 e_1 e1这条边(最大流算法为什么会流回来)

但是会走 e 2 e_2 e2这条边,而且 e 2 e_2 e2这条边包含了可行流 f 0 f_0 f0,可行流会由 e 2 e_2 e2增广到 t t t.

方法②代码LINK

有源汇上下界最小流

具体做法(两种方法)

跑无源汇可行流得到可行流 f 0 f_0 f0

先定义 e 1 e_1 e1 t t t连向 s s s的正向弧, e 2 e_2 e2 s s s t t t,是 e 1 e_1 e1的反向弧

此时 e 2 e_2 e2的流量就是可行流,然后删掉 e 1 , e 2 e_1,e_2 e1,e2的边

再由 t t t s s s跑最大流 f 1 f_1 f1,那么 f o − f 1 f_o-f_1 fof1就是答案

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 4e5+10;
const int inf = 1e11;
struct edge{
	int to,nxt,flow;
}d[maxn]; int head[maxn],cnt=1;
int in[maxn],out[maxn],ss,tt,s,t,n,m;
void add(int u,int v,int l,int r)
{
	in[v] += l; out[u] += l;
	d[++cnt] = ( edge ){ v,head[u],r-l }, head[u] = cnt;
	d[++cnt] = ( edge ){ u,head[v],0 }, head[v] = cnt;
}
int dis[maxn];
bool bfs(int ss,int tt)
{
	queue<int>q;
	for(int i=0;i<=n+1;i++)	dis[i] = 0;
	dis[ss] = 1; q.push( ss );
	while( !q.empty() )
	{
		int u = q.front(); q.pop();
		for(int i=head[u];i;i=d[i].nxt )
		{
			int v = d[i].to;
			if( d[i].flow&&dis[v]==0 )
			{
				dis[v] = dis[u]+1; q.push( v );
				if( v==tt )	return true;
			}
		}
	}
	return false;
}
int dinic(int u,int tt,int flow)
{
	if( u==tt )	return flow;
	int res = flow;
	for(int i=head[u];i&&res;i=d[i].nxt )
	{
		int v = d[i].to;
		if( d[i].flow&&dis[v]==dis[u]+1 )
		{
			int temp = dinic(v,tt,min(d[i].flow,res) );
			if( temp==0 )	dis[v] = 0;
			d[i].flow -= temp, d[i^1].flow += temp;
			res -= temp;
		}
	}
	return flow-res;	
}
signed main()
{
	cin >> n >> m >> s >> t;
	for(int i=1;i<=m;i++)
	{
		int u,v,l,r; cin >> u >> v >> l >> r;
		add(u,v,l,r);
	}
	ss = 0, tt = n+1;
	int ans = 0,res = 0;
	for(int i=1;i<=n;i++)
	{
		if( in[i]>out[i] )	add(ss,i,0,in[i]-out[i] ),ans += in[i]-out[i];
		else	add( i,tt,0,out[i]-in[i] );
	}
	add(t,s,0,inf);
	while( bfs(ss,tt) )		res += dinic(ss,tt,inf);
	if( ans!=res )	cout << "please go home to sleep";
	else
	{
		ans = d[cnt].flow;
		d[cnt-1].flow = d[cnt].flow = 0;
		while( bfs(t,s) )	ans -= dinic(t,s,inf);
		cout << ans;
	}
}