网络流学习笔记
网络流
-
一个有向图,每条边描述一个有向的限制条件,如果我们把它类比为水流的话,每条边就维护一个最大的水流量(容量)。
-
一般有一个源点(可以流出无限多的水)和一个汇点(所有流进图中的水流都会流入的点)
最大流
- 对于如上这样一个图,求最大能从源点到汇点流多少水。
EdmondKard 算法
- 每次找出一条从源点到汇点的径流,然后把途经的边容量都减去当前流量。
建立反向边,每条途经的边的反向边都加上当前流量。
重复以上操作,直到不存在任何一个新的流从源点到汇点。
tips:为了节省空间,方便后续查找,一般原边和反向边一起建,\(tot\) 从 \(2\) 开始,这样就可以通过异或运算得到某条边的反向边的编号。
Dinic 算法
-
在 \(\mathtt{EK}\) 基础上的优化。
-
将原图进行 \(\mathtt{bfs}\) 分层,然后限制每个流经过的边的端点必须是相邻层中的点,这样就可以同时搜索多个从源点到汇点的流。
通过 \(\mathtt{DFS}\) 实现具体搜索,因此可以在回溯时修改边的容量(包括反向边)。
tips:如果在搜索中发现向下的某个点能流的流量是 \(0\),可以直接通过修改 \(dep_i\) 的值将其修改为不可访问。
当前弧优化:记录一个 \(now_i\),表示走到 \(i\) 点后再深入应该从哪条边开始。最初 \(now_i=head_i\)。
这样可以避免一些无用的递归。(比如一些已经不能再产生流量的边)
tips
-
一些小技巧。
-
拆点:当题目中有一些用朴素网络流难以处理的限制条件时,我们可以考虑拆点。比如限制某一个点代表的信息只能使用 \(x\) 次这种条件。
一般拆法是将这个点拆为一个入点 \(in_i\) 和一个出点 \(out_i\),通过 \(in_i\) 到 \(out_i\) 的边的容量来限制这个条件。(如 P2766 最长不下降子序列问题)
有时需要考虑时间,按时间分层建图,可以将每个点对应时间拆成很多个点。(如 P2754 [CTSC1999]家园 / 星际转移问题) -
连边方式:比如一道很经典的问题,有 \(F\) 种食品,\(D\) 种饮品,\(n\) 个人各有喜欢的一些食品和饮品,每种食品或饮品都只能被食用一次,求最多能让多少个人满足。
解决方案为从源点向食品连一条容量为 \(1\) 的边,每种食品向喜欢它的人连一条容量为 \(1\) 的边,每个人拆成入点和出点,中间边的容量也是 \(1\),出点再向它喜欢的饮品连一条容量为 \(1\) 的边,饮品再向汇点连容量为 \(1\) 的边。(当然把食品和饮品的相对位置换一下也可以)