网络流

【什么是网络流】

一张带权图,给定了一个源点(起点)和一个汇点(终点)。

每个点比作一个中转站,源点比作一个水源,汇点比作一个洞。

每条边比作一条管道,边权比作上限。


现在要把水从源点输送到汇点,水经过若干中转站和管道到达汇点。

但是,每条管道单位时间内输送的水不能达到这条管道的上限(权)。

每个中转点所到达的水可以分流进各个管道里。

某种方案把水流到汇点的量,称作这种方案的流量。


网络流处理有关于流量的问题。

【最大流问题】

我们想要让最多的水可以流到汇点。

那么,最多有多少水可以流到汇点呢?

网络最大流算法负责处理这个问题。


【一个原始的想法】

我们的想法:能流的话,就流。

从这个图里随便找一条源点到汇点的路(增广路:一条权值都大于 \(0\) 的从源点到汇点的路径)。

这条路上的边的最小权记为 \(a\)。

把 \(a\) 单位的水沿着这条路流到汇点。

经过的所有边权,都减去 \(a\),表示它最多能输送的水又少了 \(a\)。

如果一条边的边权为 \(0\),就相当于没有了。(饱和了,不能通)

【这个想法的反例】

这个图的最大流很明显是上下分两条。但如果我们不幸搜到了中间岔开的一条,就会 WA。

而下面找到的一条路有可能会阻断上下两条。

所以,这个想法一旦选定了一条本不该选的边,就无可挽回了。


【Ford_Fulkerson】

上面说到,这个想法挂掉的原因在于不能选不该选的边。

那么,我们可以考虑让我们可以 "反悔"。

而这,就是 Ford_Fulkerson 算法思想,以下简称 FF算法。

(因为只给了理论没给程序,只能是思想而不是算法)

【FF算法】

考虑每条边再建一条反向的边。

开始时,每条反向边的边权均为 0 。

反向边的意义是:可以反悔的额度。

比如,还是这个图。

把反向边建出来,并加上权。

假如我们走了这条横跨两大陆的边,有几条黑边输送了 1 的水,要减 1.

与此同时,这些边的反向边要加 1,即可以反悔的额度加 1 了.

于是图变成这样:

我们惊奇地发现,如果加了反向边,还能找到一条路:

如果走了这条路,我们横跨大陆的错误就纠正了。

如何翻译 "走了这条路" ?

当你走了一条反向边,相当于把来到这里的水又倒了一些出来。

这些水 "反悔" 了,换了一条路,再走一遍。

【具体实现】

我们知道了整个算法的流程,但其中还有一个问题。

我们一直在说:随便找一条路,如何找呢 ?

【直接做:EK】

就按照 FF算法 说的,我们就每次找一条增广路,然后流过去。

也就是每次从源点搜索一条到汇点的路径,然后流。

这种算法就叫做 EK(Edmonds-Karp),复杂度 \(O(n^2m)\)

【图分层:Dinic】

我们进行一个优化:先从源点 BFS,只经过剩余容量 \(>0\) 的边,记录每个点的深度(距离)。

然后找增广路时,只走那些通向下一层的边。

【当前弧优化】

非常重要!

【有上下界的网络流】

【无源汇可行流】

建立超源点 \(S'\) 和超源点 \(T'\)。暂时只看每条边的下界,看每个点如果每条边都卡着下界,是多了还是少了。多了,就连一条到 \(T'\) 的边,容量为多了多少;否则 \(S'\) 连一条到这个点,容量为少了多少。

从 \(S'\) 到 \(T'\) 跑最大流,判断所有 \(S'\) 连出去的边是否都满流,如果不满流就不可行。

【有源汇可行流】

在上面图的基础上,在原来的 \(S,T\) 之间连一条 \(T\rightarrow S\) 的容量为 \(+\infty\) 的边,然后跑 \(S'\rightarrow T'\) 最大流,判断是否满流。

(这个时候我们找到了一个可行流,且这个可行流就是此时 \(T\rightarrow S\) 这条边的流量)

(想知道 \(T\rightarrow S\) 的流量,可以通过遍历 \(T\) 的所有出边,找到 \(T\rightarrow S\) 的边,然后通过 \(rev\) 看反边)

【有源汇最大流】

上面的图跑完最大流得到残余网络,在这个残余网络上先删掉 \(T\rightarrow S\) 的 \(+\infty\) 边后求 \(S\) 到 \(T\) 的最大流,这个流加上上面可行流的流量就是最大流。(不用管 \(S',T'\),因为跑 \(S,T\) 都不会用到)

注1:删边 \(\iff\) 遍历出边,把 \(T\rightarrow S\) 的 \(+\infty\) 边容量变成 \(0\),同理找到 \(S\rightarrow T\) 对应反边容量也变成 \(0\)。

注2:具体代码实现,删边可以直接调用 e[s],e[t] 的最后一个元素,因为 \(+\infty\) 边是最后加上去的。

※ 注3:也可以不删边直接在残余网络上求 \(S\rightarrow T\) 的最大流。因为 \(T\rightarrow S\) 的 \(+\infty\) 边的反边容量就刚好是第一轮最大流的流量。所以直接求 \(S\rightarrow T\) 最大流刚好会包含这条边。

【有源汇最小流】

也是残余网络删掉 \(T\rightarrow S\) 的边,但是跑 \(T\rightarrow S\) 的最大流 ,然后可行流减去这个最大流。

【费用流】

一般先保证最大流,再保证最小费用。

每条边每流一单位的流量,就要付一份这条边对应的钱。

先抛弃 Dinic,回归 FF 算法。

但是这次我们每次找增广路,用最短路算法找到一条费用最小的增广路,然后增广。

注意带费用的边的反边,费用就应该是负数了,因为反边代表的是反悔,反悔就要把费用吐出来。

复杂度:\(O(nmf)\),其中 \(f\) 等于图的最大流。

【正确性】

首先根据 FF 算法,只要是找增广路,就一定是对的。

但是是否每次找单个最小,就能得到总体最小?

是。(因为 FF 算法的反边是保证了一切反悔的可能性

【有源汇上下界最小费用最大流】

注意建图反边费用是负数!

注:所有额外边的费用都是 \(0\)。

类比有源汇最大流,我们建了图后先跑一遍 \(S'\rightarrow T'\) 最小费用最大流,设这一次得到流量 \(f_1\),费用 \(c_1\)。

然后(不删边)再跑 \(S\rightarrow T\) 的最小费用最大流,设这一次得到流量 \(f_2\),费用 \(c_2\)。

则总共最大流量是 \(f_2\),最小费用是 \(c_1+c_2\)。注意这里的费用不能直接取 \(c_2\),因为这里的费用不能直接用 \(+\infty\) 的边做巧合。

【题目】

Telecowmunication 奶牛的电信

这题是割点,不是割边。那怎么做?

把每个点拆成入点和出点。入点向出点连一条容量为删除这个点代价的边。

然后可以通信的点之间,入点向出点连 \(+\infty\) 的边。

于是就可以最小割了。

蜥蜴

最少留下 = 总 - 最多离开。

也把每一个格子分成入点和出点,对于每个格子只能经过多少只蜥蜴,其入点就向出点连多少容量的边。

互相能跳跃的格子,连容量 \(+\infty\) 的边。此时最大流就是最多离开。

除此之外,怎么分配初始蜥蜴?我们可以建立超源点 \(S\),\(S\) 向所有有蜥蜴的格子连边。

同时建立超汇点 \(T\),所有能跳出边界的出点都向 \(T\) 连边。

这题里面,我们认为格子的限制只在格子内部处理,格子之间是可以无限跳跃的。同时:我们认为蜥蜴不是一开始就在格子上,而是从一个位置 \(S\) 跳到初始格子上。

士兵占领

这题王老师的 std 写错了,把行和列的结点建到一起去了。

解法:

把士兵的分配看作都从源点出发,流到每个行,然后从每个行流到每个列上面去。

对行的限制体现在 \(s\rightarrow row\) 上,对列的限制体现在 \(col\rightarrow t\) 上,对格子障碍物的限制体现在 \(row\rightarrow col\) 上。用上下界网络流做。

具体而言:设第 \(i\) 行有 \(x\) 个格子可以放,则 \(s\) 向行 \(i\) 代表结点连一条容量 \([l[i],x]\) 的边。设第 \(j\) 列有 \(x\) 个格子可以放,则列 \(i\) 代表结点向 \(t\) 连一条容量 \([c[i],x]\) 的边。

然后如果 \((i,j)\) 可以放士兵,则行 \(i\) 代表结点向列 \(j\) 代表结点连一条 \([0,1]\) 的边。

然后求有源汇上下界最小流

圆桌问题

构造一个图:左边 \(n\) 个点,右边 \(m\) 个点,代表单位和桌子。

\(s\) 向左边每个点连边,容量 \(r_i\);右边每个点向 \(t\) 连边,容量 \(c_i\);左边每个点向右边每个点连边,容量 \(1\)。

跑最大流,看看是否等于总人数。

如何找方案:循环左边每个点,再循环到右边的边,看看容量是否为 \(0\)。

星际转移问题

之前 ZR 模拟做过类似的。

把地月看作 \(n+1,n+2\),洪水填充判断是否有解。

拆点,每个点拆成 \(t\) 个点表示在不同的时间到这个点的状态。

初始源点向地球连 \(+\infty\) 的边,表示允许任意多人到地球。求最短时间就是看第一个时间点,到达月球的人数达到 \(k\)。

枚举 \(i=1\sim +\infty\),然后枚举每艘太空船,找到它第 \(i\) 时刻所在位置,然后从 \(i\) 时刻位置向 \(i+1\) 时刻所在位置连容量 \(h_x\) 的边。\(i\) 时刻的月球向汇点连 \(+\infty\) 边。

加了新边之后跑最大流,把最大流加入目前的人数中,判断是否达到 \(k\) 人。如果是,就输出答案;不是,进入下一轮。

植物大战僵尸

神奇的建模。

先拓扑排序找出所有环和环的后继。这些一定是不会被吃的。

如果保护者指向被保护者,答案就是在剩下的点之间求最大权闭合子图。

(闭合子图:选一些点,使得如果 \(u\) 被选,则 \(u\) 的所有前驱都要选)

建模:建立超源超汇,原来保护关系的边都方向取反后 变成 \(+\infty\) 的边。

然后,若点 \(u\) 权值为正,\(S\rightarrow u\),容量 \(a_u\);否则 \(u\rightarrow T\),容量 \(-a_u\)。

去掉拓扑排序后,所有收益为正的植物的收益之和 减去 最小割即答案。

拍照

也是最大权闭合子图。

对 \(m\) 个拍照事件做出 \(m\) 个点,\(n\) 个人做出 \(n\) 个点。

类比上面,每个人都保护了一些拍照事件。

海拔

观察:最终每个点的高度要么是 \(0\),要么是 \(1\)。且最后的花费就是所有 \(0\rightarrow 1\) 的边的人流量。

观察:最后的图中,\(0,1\) 各形成一个连通块。

所以我们的问题:分成两个连通块,使得联通它们的边边权和最小。

直接最小割,但是发现还要优化。

对偶图!

对偶图是一种平面图最小割的优化方法。可以把最小割转化为最短路。可以上网搜。

CF704D

思路和蜥蜴一样,每个点的颜色视作先从超级源点流到每个 \(x\) 坐标上,再从 \(x\) 坐标流到 \(y\) 坐标上。然后就变成上下界网络流。

点要离散化,但是不能用 map,因为点可能重复.

餐巾计划问题

补充题意:初始没有餐巾;最后一天过了,餐巾是不是全部干净都行。

把每一天看成节点,餐巾的使用视作在节点之间流动。

具体而言,建立 \(2N\) 个点。每一天对应两个点,分别表示在这一天内干净的餐巾和脏的餐巾。

(以下没说费用都是 \(0\))

如何连边?首先,干净点 \(A\) 向干净点 \(A'\) 连 \(+\infty\) 的边,表示这天可以选择任意数量餐巾不用;脏点 \(A\) 向脏点 \(A'\) 连 \(+\infty\) 的边,表示这天可以选择任意数量餐巾不洗。

\(s\) 向 \(1\) 连边,容量 \(+\infty\),费用 \(p\),表示买餐巾。

\(i\) 向 \(i'\) 连边,容量 \([r[i],r[i]]\),表示第 \(i\) 天会产生 \(r[i]\) 个脏餐巾。

\(i'\) 向 \(i+m\) 连边,容量 \(+\infty\),费用 \(f\),表示快洗;

\(i'\) 向 \(i+n\) 连边,容量 \(+\infty\),费用 \(s\),表示慢洗。

\(n\) 和 \(n'\) 向 \(t\) 连边,容量 \(+\infty\)。

跑上下界最小费用流即可。

深海机器人问题

难点在于怎么处理每个生物标本只能被采摘一次。

首先生物标本是在点上的,对点的限制不好直接在图上体现。我们用一个经典 trick: 入点出点。

然后对于一个生物标本,我们从入点到出点连一条容量 \(1\) 的边,费用是生物标本的价值。

另外每个入点到出点,都连一条容量无穷,费用 \(0\) 的边。

跑最大费用最大流。

志愿者招募

我们把问题看作我们一开始有无限个志愿者。题目的"雇佣"就是让一些志愿者开始工作。设置 \(n+1\) 天,每一天抽象为一个结点,志愿者视作随着时间(天数)流动。

假如第 \(i\) 天需要 \(p_i\) 人,则从 \(i\) 天向第 \(i+1\) 天连一条容量为 \(inf-p_i\) 费用 \(0\) 的边,表示第 \(i\) 天最多有 \(inf-p_i\) 人休息。

一类志愿者可以从 \(s_i\) 做到 \(t_i\) 天,则从 \(s_i\) 向 \(t_i+1\) 天连容量 \(+\infty\) 的边,费用 \(c_i\)。