网络流2:上下界可行流
网络流2 上下界可行流
无源汇上下界可行流
概念定义
对于一流网络 \(G=(V,E)\),每条边 \((u,v)\) 存在容量上界 \(c_u(u,v)\) 和容量下界 \(c_l(u,v)\),且不存在源点和汇点。
与原定义类似,满足容量限制和流量守恒的流称之为可行流。
- 容量限制:在原定义的基础上,更改容量限制的定义:该可行流在任意边 \((u,v)\) 的流量 \(f(u,v)\) 满足 $c_l(u,v) \le f(u,v) \le c_u(u,v) $,则该流满足容量限制。
- 流量守恒:与原定义相同,但是由于没有 \(s\)、\(t\),所以所有点都需满足流量守恒。
思路推导
对于一对流网络和可行流 \((G,f)\),我们期望构造一对新的流网络和可行流 \((G',F')\) 满足与 \((G,f)\) 一一对应。
对于容量下界,我们十分讨厌。所以不妨在不等式两边同时减去容量下界,使得 $0 \le f(u,v)-c_l(u,v) \le c_u(u,v)-c_l(u,v) $,并记 \(f'(u,v) = f(u,v) - c_l(u,v)\),\(c'(u,v) = c_u(u,v)-c_l(u,v)\)。此时 \(f'\) 满足 $0 \le f'(u,v) \le c'(u,v) $,即满足容量限制。
但是,对于一点 \(u\),其流入量会减少 $\sum _{v \in V} c_l(v,u) $,流出量会减少 $\sum _{v \in V} c_l(u,v) $,净差 \(w_u\) 等于 $\sum _{v \in V} c_l(v,u) - \sum _{v \in V} c_l(u,v) $。
此时,不妨建立虚点 \(S'\) 和 \(T'\),若
- \(w_u = 0\),则无需操作。
- \(w_u < 0\),则连接边 $c'(S',v) = -w $
- \(w_u > 0\),则连接边 $c'(u,T') = w $
如果附加边满流,说明这一个点的流量平衡条件可以满足,否则这个点的流量平衡条件不满足。
此时进行 \(S'\) 到 \(T'\) 的最大流,若附加边满流则存在可行流,否则不存在。
代码:
using Dinic::init;using Dinic::add;using Dinic::dinic;
int cl[M],cu[M],w[M];
void mian()
{
init();
read(n),read(m);
int S=0,T=n+1;
for(int i=1;i<=m;i++)
{
int u,v;
read(u),read(v),read(cl[i]),read(cu[i]);
cu[i]=cu[i]-cl[i];w[u]+=cl[i];w[v]-=cl[i];
add(u,v,cu[i]);
}
int tot=0;
for(int i=1;i<=n;i++)
{
tot+=abs(w[i]);
if(w[i]>0)
{
add(i,T,w[i]);
}
else if(w[i]<0)
{
add(S,i,-w[i]);
}
}
tot/=2;
int res=dinic(S,T);
if(res!=tot) puts("NO");
else
{
puts("YES");
for(int i=0;i<m*2;i+=2)
{
printf("%d\n",Dinic::e[i^1].c+cl[i/2+1]);
}
}
}
有源汇上下界可行流
与前问相比新增了源点 \(s\) 和汇点 \(t\)。请注意区分,我们的虚拟点是 \(S\) 和 \(T\)。
不妨连接 $c(t,s) = \infin $,这样这两点也流量守恒了
代码易写。
有源汇上下界最大流
与前问相比需要在满足容量限制的情况下使得源点 \(s\) 到汇点 \(t\) 的流尽可能大。
首先是需要跑一个可行流。然后删除掉辅助边(正常就不用管了)和 \((t,s)\)。然后,跑一个 \(s\) 到 \(t\) 的最大流即可。
证明大致如下:核心难点在于 \(S\) 到 \(T\) 的无增广路不等于 \(s\) 到 \(t\) 的无增广路,因为 \(S,T\) 不满足流量守恒。但是考虑到增广路的“底层”是加法,而这里若将两个流做减法,就会发现由于这两个流都是可行流,所以 \(S\) 和 \(T\) 都是满流,所以相减后容量为零,这两点就满足容量守恒了。
代码
using Dinic::init;using Dinic::add;using Dinic::dinic;
int cl[M],cu[M],w[M];
void mian()
{
int t,s;
init();
read(n),read(m),read(s),read(t);
int S=0,T=n+1;
for(int i=1;i<=m;i++)
{
int u,v;
read(u),read(v),read(cl[i]),read(cu[i]);
cu[i]=cu[i]-cl[i];w[u]+=cl[i];w[v]-=cl[i];
add(u,v,cu[i]);
}
int tot=0;
for(int i=1;i<=n;i++)
{
tot+=abs(w[i]);
if(w[i]>0)
{
add(i,T,w[i]);
}
else if(w[i]<0)
{
add(S,i,-w[i]);
}
}
tot/=2;
add(t,s,INF);
int res=dinic(S,T);
if(res!=tot) puts("No Solution");
else
{
res=Dinic::e[Dinic::ecnt].c;
Dinic::e[Dinic::ecnt].c=Dinic::e[Dinic::ecnt-1].c=0;
res+=dinic(s,t);
printf("%d\n",res);
}
}
有源汇上下界最小流
类似的,只是将最大流变为最小流,可以为负数。
根据定义,不难想到转化:$f(s,t)=-f(t,s) $,那么最小的 $f(s,t) $ 不就是最大的 \(f(t,s)\) 吗?
代码
using Dinic::init;using Dinic::add;using Dinic::dinic;
int cl[M],cu[M],w[M];
void mian()
{
int t,s;
init();
read(n),read(m),read(s),read(t);
int S=0,T=n+1;
for(int i=1;i<=m;i++)
{
int u,v;
read(u),read(v),read(cl[i]),read(cu[i]);
cu[i]=cu[i]-cl[i];w[u]+=cl[i];w[v]-=cl[i];
add(u,v,cu[i]);
}
int tot=0;
for(int i=1;i<=n;i++)
{
tot+=abs(w[i]);
if(w[i]>0)
{
add(i,T,w[i]);
}
else if(w[i]<0)
{
add(S,i,-w[i]);
}
}
tot/=2;
add(t,s,INF);
int res=dinic(S,T);
if(res!=tot) puts("No Solution");
else
{
res=Dinic::e[Dinic::ecnt].c;
Dinic::e[Dinic::ecnt].c=Dinic::e[Dinic::ecnt-1].c=0;
res-=dinic(t,s);
printf("%d\n",res);
}
}
参考资料
忘了……这篇文章好久之前写的,也没有进行大改。到现在还有点半成品,但是沃咕了
私密马赛 (>. <;)
updaye on 7/14
优化了代码,对有源汇上下界最小流添加了一句极为简短的转化分析

浙公网安备 33010602011771号