[学习笔记]当网络流遇上了上下界
〇、前言
每次到 \(\sf TryMyEdge\) 和同学们的名场面,战线就会太长,我就跟不上了......
然后我就只能脱离战线了 被迫当逃兵 TAT。
还有一些学习使用的资料 战线过长,资源补给不到位,只能自给自足了 。
Menci's Blog、\(\text{NOI Ag}\) 的学长.
至于一些代码什么的,能补就补,补不上就......就这样吧。
壹、无源汇可行流
1.1.问题描述
我们要解决的问题是:
有 \(n\) 个点 \(m\) 条边,每条边有个流量下界和上界,要求图中每条边都要达到下界,不能超过上界,无源汇点,求这样的方案。
1.2.建图
这种问题怎么搞?实际上我们只需要这样建图,定义一个炒鸡源点 \(ss\),炒鸡汇点 \(tt\),对于一条边 \(e=\lang u,v\rang,flow(e)\in [low,c]\),这样连
对于我们新建出来的图直接跑从 \(ss\) 到 \(tt\) 的最大流就可以了,如果对于每条 \(ss\rightarrow u\) 的点,以及 \(v\rightarrow tt\) 的点都跑满了,就是存在解的,解该怎么找就不多说了。
但是为什么这样是可行的呢?从含义上讲,由于每条边至少都要向 \(v\) 流一个 \(low\) 的流,我们就先强制满足这样的要求,而对于 \(u\),它至少要流出 \(low\),我们也让它满足这样的要求,而这条边剩下的一些容量 \(c-low\),就放在网络里面自行调整。如果把 \(ss,tt\) 想象成 传送门/中转站 的话,可能会更形象。
1.3.一些优化
考虑到我们有 \(m\) 条边,每条边要搞出 \(3\) 条额外边,但是我们很有可能搞出这样的情况
实际上我们可以将 \(ss\rightarrow u\) 的两条权值分别为 \(a,b\) 的边合并成一条权值为 \(a+b\) 的边,对于 \(u\rightarrow tt\) 亦是如此,更厉害地,我们可以将 \(a,b,x,y\) 都合并,分两种情况讨论:
- \(a+b\ge x+y\),那么我们只需连 \(ss\rightarrow u\),容量为 \(a+b-x-y\);
- \(a+b< x+y\),那么我们只需连 \(u\rightarrow tt\),容量为 \(x+y-a-b\);
然后直接跑网络流最大流,判断所有从 \(ss\) 出,入 \(tt\) 的边是否满流即可,要构造方案也很简单。
1.4.代码
贰、有源汇可行流
2.1.问题描述
和 1.1. 差不多,多了个源点 \(s\) 和汇点 \(t\).
2.2.建图
考虑如果存在这样的可行流,那么就有从 \(s\) 出去一些流,进入 \(t\) 一些流,如果我们将 \(t\rightarrow s\) 连一条 \(+\infty\) 的边,流进 \(t\) 的流又流回 \(s\),那这不就是无源汇可行流了吗?
所以,解决办法就是:先像 壹 中连边,然后再连 \(t\rightarrow s\) 的边,容量为 \(+\infty\),然后跑最大流。
最后,原图上从 \(s\) 向 \(t\) 的流量就是建出的图上 \(t\rightarrow s\) 的流量。
叁、可行最大流
3.1.问题描述
建立在 2.1. 的基础上,求在满足上下界要求的情况下,从 \(s\) 到 \(t\) 的最大流。
3.2.转化
最大流不是流?所以我们要先用 贰 判断是否有解。
如果有解,那么我们当前所得到了一个可行流,这个流中,满足了每条边的下界,对于这个烂摊子我们怎么办呢......
直!接!从!\(s\)!向!\(t\)!增!广!
这个是什么意思呢?本来我们的源汇点是 \(ss\) 和 \(tt\),但是跑完之后我们发现它们俩没用了,因为如果存在可行流,我们现在就要求的是最大流,由于 \(ss\) 没有入边,\(tt\) 没有出边,所以我们直接增广 \(s\rightarrow t\) 的时候不会将他们俩考虑进去,换句话说,我们不会把边的下界都破坏掉 —— 因为从 \(ss\) 连出和连入 \(tt\) 的边都是保证下界的,我们不会增广他们即不会破坏他们的边,进一步说不会破坏所有边的下界保证。
所以先跑可行流,如果有就再在这个烂摊子上跑从 \(s\) 到 \(t\) 的 \(\tt dinic\) 就可以了。
3.3.代码
肆、可行最小流
4.1.问题描述
和 3.1. 没啥区别,求最小流就是了。
4.2.转化
类似 3.2. 我们先求可行流,得到一个烂摊子,然后,我们只需要跑反向最大流,用可行流减去反向最大流就可以了......但是注意需要把之前 \(t\rightarrow s\) 的 \(+\infty\) 的边去掉,不然就嘿嘿嘿了。
很简单的转化。
4.3.代码
伍、无负环可行费用流
5.1.无源汇可行可行最小费用流
考虑模仿 1.2. 的建图,对于一条边 \(\lang u,v,w,c\rang,flow\in [low,c]\),我们这样建
然后直接跑从 \(ss\) 到 \(tt\) 的最小费用最大流即可。
5.2.有源汇可行最小费用最大流
先用满足下界,但是又有源汇,所以我们考虑将 2.2. 和 5.1. 结合起来。
大致就是用 5.1. 方式建边,然后对于 \(s,t\) 我们连一条 \(t\rightarrow s\) 的,容量为 \(+\infty\) 而费用为 \(0\) 的边。
然后直接跑最小费用最大流,得到烂摊子之后,再跑 \(s\) 到 \(t\) 的最小费用最大流,有点像把问题 贰、叁、5.1 全部结合起来的味道。
5.3.有源汇可行最小费用流
先满足每条边的下界,用 5.2. 的方法,对于 \(s,t\) 我们连一条 \(t\rightarrow s\) 的,容量为 \(+\infty\) 而费用为 \(0\) 的边,然后跑从 \(ss\rightarrow tt\) 的最小费用最大流满足下界,然后对于烂摊子跑从 \(s\rightarrow t\) 的最小费用流。
为什么后面还要跑最小费用流?因为有可能从 \(s\rightarrow t\) 的流有可能是负的花费,至于这个负的花费,因为我们跑费用流会激活一些负权边。
5.4.有源汇可行最小费用最小流
先用 5.3. 跑出一个可行最小费用流,然后从 \(t\rightarrow s\) 跑反图,跑最小费用最大流。
陆、有负权费用流
它已经没有 "可行" 二字啦(每条边没有上下界了)!但是思路有相似性。
先考虑没有源汇点的情况。
负环当然是好东西,它可以让你的费用变小,所以我们有一种想法 —— 一个一个把所有负环都找出来,然后加到答案里面去。找环已经很困难了,我们还要一个一个找,这个算法肉眼可见的 \(\mathcal O(\tt{TLE})\).
能否由贪心入手?贪心地选择每个负权边并把他们流满,但是作为流它可能不合法,这个时候我们就需要调整,将其调整为合法的流,而说到调整,有没有想到 1.2. 里面的东西:
而这条边剩下的一些容量 \(c-low\),就放在网络里面自行调整。
思路相似由此可见,但是亦有差异:
处理可行流,我们强制流满(下界),并且不可反悔。
而有负权环,我们一开始流满(上界),但是可以 "退款"。
那么我们怎么实现退款?这里,\(\text{NOI Ag}\) 的学长提出了他的方法:
根据斜对称性,一条边的流的减少等价于它反向边的流的增加。
也就是说,对于一条边 \(\lang u,v,w,c\rang (w<0)\),它反向边的容量就是它 "可退款" 的数量,而既然我们要实现可以退全款,就让它和它的反边初始容量都为 \(c\) 就可以了。
由于我们是将所有负流的贡献全部加起来,得到一个负边流满的 \(ans\),即
然后我们加上调整所不得不选上的正权边,对于一条边 \(\lang u,v,k,c\rang (k<0)\),考虑这样建图
然后跑最小费用最大流,得到 \(cost\),最后的答案就是 \(cost+ans\).
如果有源汇点?嘿,往 \(t\rightarrow s\) 连一条容量 \(+\infty\) 边权为 \(0\) 的边,这难道不就转化成无源汇的问题了吗?
柒、总结
这些东西排列组合,可以把人搞晕......
但是本质上都是差不多的,对于只有上下界的情况,我们直接使用 壹、贰 。
对于又有上下界又有费用要求,首先我们需要的肯定是满足下界,所以先用 壹、贰 的方法(带上边权)跑最小费用最大流,然后再根据要求进行调整。
而负环这东西,使用 "正难则反" 的思路,转化为了先流满所有负边,然后调整负边成为合法流的 "无源汇可行流" 的情况。