注 意 \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 inx−outx的边
含义是,因为流入 x x x的流量多出了 i n x − o u t x in_x-out_x inx−outx,需要为这些多出来的流找个去处
那么就让 s s ss ss输入给 x x x这么多流量,意味着 x x x作为出点的那些边从 x x x这里多拿走 i n x − o u t x in_x-out_x inx−outx的流量
这些从 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,由 x x x连向 t t tt tt一条流量 o u t x − i n x out_x-in_x outx−inx的边
含义是,因为流出 x x x的流量多了 o u t x − i n x out_x-in_x outx−inx,那这部分流量哪里来的呢?
那么就让 x x x输入 t t tt tt这么多流量,意味着在 x x x作为入点的那些边需要多输入 o u t x − i n x out_x-in_x outx−inx的流量
哪些边流给 x x x分别流多少不关心,只关心最后总共是否流入了 o u t x − i n x out_x-in_x outx−inx给 x x x,那么 x x x再流向 t t t
这样从源点 s s ss ss跑最大流,若所有 s s ss ss的出边满流说明有解,否则无解。
有解时,每条边的流量是流量下限加上这条边的附加流
有源汇上下界可行流
⚪ \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.
有源汇上下界最小流
具体做法(两种方法)
跑无源汇可行流得到可行流 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 fo−f1就是答案
#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;
}
}