网络流学习笔记

说明

  1. 如果没有特殊说明,本文中的“图”一律只“网络”。
  2. 如果提到 \(c(u,v),f(u,v)\),则默认忽略 \(u,v\) 没有直接连边的点对。
  3. 本人比较菜(只有初一,而且这是第一次写),可能有一些问题。请多多谅解。

网络

给你一张有向图 \(G=(V,E)\),有 \(n\) 个节点 \(m\) 条边,每条边有一个容量(即通过这条边的流量不能超过这个限制,\((u,v)\) 这条边的容量记为 \(c(u,v)\),通过其的流量记为 \(f(u,v)\),则有 \(0\le f(u,v)\le c(u,v)\))。称起点为源点(记为 \(s\)),终点为汇点(记为 \(t\))。除这两个点以外其他节点都不能存储流量(即净流量为 \(0\)),求汇点能接收的最大流量。

一些定义

节点 \(u\) 的净流量为 \(\sum_{x\in V}f(x,u)-\sum_{x\in V}f(x,u)\)
从边集 \(E\) 到整数集或实数集的函数(这句话摘自 OI-wiki,可以理解为对每条边规定一个流量),满足上面的所有条件,则称其为。这个流的流量为 \(s\) 的净流量。流量最大的流被称为最大流
割掉一些边,使得这张图的最大流为 \(0\),剩下与 \(s\) 相连的点属于集合 \(s\),与 \(t\) 相连的点属于集合 \(T\)\({S,T}\) 就是这张图的一个 \(s-t\) 。这个割的容量为 \(\sum_{x\in S}\sum_{y\in T}c(x,y)\)

最大流

求法

我只会 dinic,所以这里只讲 dinic。这里令我们当前求出来的流为 \(f\)
我们把原图中所有节点和剩余流量 \(>0\) 的边拿出来组成一张新的图,这张图被称为残量网络
和二分图匹配类似,网络流中也存在增广路。如果我们能在残量网络中求出来一个流 \(f_b\)(流量 \(\ge 0\)),那么我们称 \(f_b\) 为一条增广路。为了让答案更优,我们令 \(f\gets f+f_b\),重复执行直到不存在增广路。这个过程我们称之为增广
但是由于增广路是随便找的,答案可能不是最优,所以我们引入反悔贪心得思想。我们建一条反向边,并令 \(f(v,u)=-f(u,v)\),这样如果我们选择走 \((v,u)\) 这条边,相当于撤回了 \((u,v)\) 这条边的部分流量。
然后我们发现流量可能为负。但是这样没关系,因为真正有用的是剩余的流量。如果实在不能理解可以看做每次给边 \((v,u)\) 加上 \(w\) 的流量,则给它的反向边加上 \(w\)容量
引用一张 OI-wiki 上的图配合理解。

注意到当我们进行两次增广时,中间的边的流量互相抵消了,所以答案是正确的。
然后我们考虑怎么求增广路。
首先用 bfs 对残量网络分层,每个节点的流量只能给它下一层的节点。然后直接 dfs,遇到能给流量的节点就给尽可能多的流量,注意要更新每条边的信息。这样我们就求出了一条增广路,但是这样太慢了。
注意到一张残量网络可能同时存在多条增广路,我们可以在 dfs 时把它们一起求出来同时增广。这个技巧我们称之为多路增广。
然后注意到如果已经不能通过一条边找到增广路,我们可以直接不访问这条边。而且按照我们遍历的顺序,这些边一定排在最前面(也就是一个前缀),所以我们可以直接用一个指针来维护。这个技巧我们称之为当前弧优化。
dinic 单次增广是 \(O(|V||E|)\) 的,增广次数是 \(O(|V|)\) 的,所以总时间复杂度是 \(O(|V|^2|E|)\) 的。(我不会证)而且 dinic 在某些特殊的图上跑得非常快,所以判断是否可行不能只算最高复杂度。
附上 dinic 的实现。

例题 P1402

话说为什么这道题是紫。
这道题我们讲匹配的题目中图论建模加网络流的基础。

节点的作用

源点和汇点一般是无意义的。
对于一般的节点,其可以代表一个元素(或区间等)等,根据题目需要确定。
在本题中每个节点代表房间,顾客和菜中的一个。

容量的作用

对于一般的匹配题来说,容量表示需要的匹配次数。
如本题中所有的边容量都为 \(1\)
注意到对于某一些节点,我们无法表示其最大匹配次数(如本题中的顾客)。我们想要让这个节点中间只通过 \(1\) 的流量,但是如果这样建图的话是无法限制的。注意到其它边限制流量的方法是设置一个容量,所以我们考虑仿照它也设定一个容量。但是对于节点的容量不好操作,所以我们把这个容量转到边上面。将一个点拆成两个点,中间连边表示次数限制,其余链接原来这个点的边按方向连到两个点上。这个技巧叫做拆点。
所以说很简单对吧。

最大流最小割定理

这个定理指出,最大流等于最小割。下面给出证明(选自我们学长的课件,因为我也不会证,个人感觉没什么用,你只需要知道最大流能求最小割就可以了):
首先对于 \(G\) 中任意 \(s\)\(t\) 的流,其流量不超过 \(G\) 的最小割,因为 \(L\) 流出的总流量肯定是不超过 \(L\to R\) 之间的最大流量的。因此最大流不超过最小割。
其次若一个流对应了无增广路的残量网络,则它也对应了一个不超过 \(f(t)\) 的割。考虑将其中 \(s\) 能到达的点设为 \(L\),剩余为 \(R\),则 \(L\to R\) 的边必然满流,所以 \(f(t)\) 不小于这组割。
由此可得最大流可以得到最小割,即证明了最大流与最小割相等,并给出了构造。

例题 P3749

这道题我们讲图论建模加最小割的技巧。

节点的作用

只是用来表示每个信息关联的两个元素,或者把所有边串起来。
本题中的节点表示选不选某个区间和是否选了这个类型。

容量的作用

每条边表示一个信息,一般容量表示割掉的代价。
不难发现如果有解的话最小割一定不会选择容量为 \(inf\) 的点。所以对于不能删掉的边,我们将其的容量设为 \(inf\)。同理如果必须割我们也可以连边,让这种情况如果不割掉我们连的这条边就不是割。
通常,如果代价为负(可以看做收益为正)源点向其连边,否则(可以看做收益为负)向汇点连边。记得对代价取绝对值。这个结合后面的双选模型可能更好理解。
本题中直接套用即可,不难发现选了一个区间必须选它包含的所有子区间和类型。

上述方法的应用

双选模型

对于每个元素你需要给他规定一个集合(二选一),如果选第一个集合则代价为 \(a_i\),否则代价为 \(b_i\)。如果两个元素选了不同集合则会产生 \(c_{i,j}\) 的代价。最小化总代价。
考虑用上面提到的技巧来做。我们连边 \((s,i,a_i),(i,t,b_i)\)。(最小割的定义就是分成两个集合,割掉哪条边相当于分到对应的集合)。然后连边 \((i,j,c_{i,j})\) 即可。

切糕模型

有一个矩阵,你需要对每个位置规定一个 \([1,m]\) 中的数,位置 \((i,j)\) 的数为 \(k\) 时会产生 \(c_{i,j,k}\) 的代价,且相邻格子的数只差不能超过 \(d\),最小化总代价。
对于每个位置建一条长度为 \(m\) 的链,首尾分别和 \(s,t\) 相连,割掉哪条边就表示选哪个值。每条边的容量为对应代价,反向变容量 \(inf\)
然后对于相邻的格子,在数值之差为 \(d\) 的两个节点(即映射到同一条链上,中间有 \(d\) 条边)建一条容量为 \(inf\) 的边。注意到最优解一定不会割这条边,所以我们就满足了题目的条件。

最大闭权和子图

有一些物品,选了一个物品就要选一些其他物品,每个物品有一个收益(可能为负),最大化总代价。
直接套用上述思路即可(没有任何变化)。

费用流

在网络流的基础上对每条边加一个单位费用 \(w(u,v)\),即这条边通过 \(f(u,v)\) 流量的费用为 \(f(u,v)\times w(u,v)\)。最小费用最大流是在流量最大的基础上使费用最小。

求法

采用贪心,每次寻找费用最小的增广路进行增广即可。沿用求最大流的思路,只需要将 bfs 改为最短路即可。
注意一下实现细节,退回流量时还要还钱。

例题 P1251

这道题很板,应该能一眼看出来吧。
讲一下建模思路。先把每个点拆成早上和晚上。
首先我们知道每天的餐巾数量要能达标,所以我们把这个节点的早上向汇点连边,容量为餐巾数量,费用为 \(0\)。如果满流了说明达标。
然后源点向每天的晚上连边,容量为餐巾数量,费用为 \(0\),代表每天产生这么多的脏餐巾(注意如果不拆点的话我们会把脏餐巾和干净的餐巾混用,即将从源点获得的脏餐巾给了汇点。不从白天获得的原因是白天的餐巾是给汇点的,不能给晚上)。如果这些边没满流也没关系,就相当于丢掉了一些餐巾。
然后购买餐巾很好理解,源点向早上连边,容量为 \(inf\),费用为 \(p\)。快洗部和慢洗部就是每天的晚上向对应天数后的早上连边,容量为 \(inf\),费用为对应的价钱。这些应该都能想出来。
然后直接跑最小费用最大流即可。

上下界网络流

每条边除了容量上限还多了一个下限(记作 \(b(u,v)\))。要求所有条件都满足。

无源汇上下可行流

即没有源点和汇点,所以我们先建出源点和汇点。
首先令所有的边都有 \(b(u,v)\) 的流量,然后建出残量网络。
对于每个节点:
\(M_i=\sum_{x\in V}f(x,u)-\sum_{x\in V}f(x,u)\)

  1. 如果刚好流量守恒,则不需要任何操作。
  2. 如果出流量过大,则将这个点向 \(t\) 连边,容量为 \(-M\)
  3. 否则 \(s\) 向这个点连边,容量为 \(M\)

然后跑一遍最大流,如果 \(s\) 的所有出边都满流说明有解,否则无解。
讲解一下为什么这样建图:

  1. 如果 \(M_i>0\),那么说明点 \(i\) 多了 \(M_i\) 的流量需要传出去,但是在网络流中节点没有初始流量,所以我们让它从源点获得等量的流量。
  2. 如果 \(M_i<0\),那么点 \(i\) 还欠了 \(-M_i\) 的流量,这些流量不能给出去,但是因为不能存储流量,所以我们把这些流量给汇点。

注意到这样建边有解的要求是所有有关 \(s,t\) 的边都满流,但是根据流量守恒,我们只需要判断 \(s\) 即可。

有源汇上下可行流

多了源点和汇点,注意到只有 \(s\)\(t\) 可以不满足流量守恒。但是注意到 \(s\) 的出流量等于 \(t\) 的入流量,所以我们建一条 \((t,s,0,inf)\) 的边,将 \(t\) 多的流量给 \(s\) 即可转化为无源汇可行流。
若有解,则 \(S\)\(T\) 的可行流流量等于 \(T\)\(S\) 的附加边的流量.

有源汇上下最大流

尽可能多的给流量。
跑一遍可行流,建出残量网络(删去所有附加边)转化为最大流,然后两者相加即可。

有源汇上下最小流

考虑退掉我们不用的流量。
先跑可行流,然后反向建边,每条边的容量为可行流中的流量 \(-\) 流量下限,反向边的容量为原来这条边的容量 \(-\) 可行流中的流量,然后跑一遍 \(t\)\(s\) 的最大流,答案相减即可。

例题 P4043

这道题第一眼看上去不像网络流,所以我们重点来将怎么想到网络流的。
很容易看出这是一张有向无环图,走几遍就要几遍的代价,而大部分算法都无法(或者说不方便)处理这个,但是费用流可以(向这种什么算法(或数据结构,思想等)适合处理什么类型的问题都要熟悉)。于是我们想到用费用流做。
然后注意到每条边都要至少走一次,所以我们想到可以通过上下界网络流来约束。两个合起来就是上下界有源汇最小费用最大流。(具体实现和上下界有源汇最大流差不多)
然后直接做就可以了,注意因为汇点只能有一个,所以我们需要新建一个汇点,其他点都想这个点连边(注意不是只有剧情结尾要连边,因为我们可以中途退出)。
其实这道题就是可重集的路径覆盖问题。

总结

其实网络流背板子就可以了,重点是如何想到通过网络流的一些性质来建图。多练吧。

posted @ 2026-07-18 15:05  虚空远行者  阅读(21)  评论(0)    收藏  举报