网络流学习笔记
此博客主要顺序依照AcWing算法进阶课。
若有不足请多包涵与指教。
真速通了吗?如速!
最大流
模板——EK
每次 bfs 找一条从源点到汇点的增广路,然后将增广路上的所有边减去流量,加进反向边。因为要给以后的寻找增广路提供反悔的机会。
每找到一条增广路都将此时汇点的流量加进答案里。
时间复杂度 \(O(nm^2)\)。(实际远远达不到这个复杂度)
模板——dinic
每次 bfs 构建分层图(忽略边权为 \(0\) 的边),分层图即按照 dep 分层(不是这个)。
多次从源点开始在分层图上进行 dfs,每次往下输送流量,直到该分层图找不到新的增广路。这样可以一次找多条增广路,而 EK 一次只能找一条增广路。
优化:
-
已经在当前分层图上搜过的边,就不用再搜第二遍了,因为这条边的流量肯定已经被“榨干”了。
-
当当前点往下输送的流量已经等于流进该点的流量时,要及时 break,因为往下输送 \(0\) 流量是没有意义的。
时间复杂度 \(O(n^2m)\)。(实际远远达不到这个复杂度)
模型——二分图匹配
源点向所有左部点连边,所有右部点向汇点连边,其它正常连边。注意所有边的容量都为 \(1\)。
Question:为什么不用匈牙利算法?
匈牙利的时间复杂度为 \(O(nm)\),而 Dinic 求解二分图匹配的时间复杂度是 \(O(m \sqrt{n})\)。
时间复杂度证明:
因为每条边的容量都为 \(1\),所以每次 dfs 增广的时间复杂度都是 \(O(m)\)。
显然前 \(\sqrt{n}\) 次增广的总复杂度为 \(O(m \sqrt{n})\),之后的增广路径长度至少为 \(O(\sqrt{n})\),而这样的路径数量不会超过 \(O(\sqrt{n})\) 条。
输出路径:剩余流量为 \(0\) 代表这条边被选了。
模型——二分图多重匹配
代表就是左部点,圆桌就是右部点。
首先所有桌子都能坐,但是一张桌不能坐两个相同的代表,所以所有左部点分别向所有右部点连一条容量为 \(1\) 的边。
源点向所有左部点连一条容量为那个点人数的边,所有右部点向汇点连一条容量为那张桌子容量的边。
无解:最大流量不等于人数总和。
模板——无源汇上下界可行流
正常的最大流,设流量为 \(x\),则 \(0 \le x \le lim\)。
有了上下界的规定,流量必须满足 \(l \le x \le r\)。
考虑转换,因为将不等式同时减一个数,不等式仍成立,所以使它们都减去 \(l\),就变成了 \(0 \le x-l \le r-l\)。
因为要满足流量守恒,所以每个点入边的流量和要等于出边的流量和。
建出虚拟的源点和汇点,如果某个点的出边流量和多,就让源点向它连一条容量为差值的边,反之让这个点向汇点连一条容量为差值的边。若相等就不用连边。
至于输出用了多少流量,就是它的反向边的剩余流量(它用了多少流量)加上它的流量下界。
模板——有源汇上下界最大流
跟上题相比多了源汇点,而且求的是最大流。
还是按照上题的思路建出虚拟源汇点。
首先判断有无解,在上题基础上再 add(t, s, inf) (小写表示原图的源汇点),然后看 \(S\) 到 \(T\) 的最大流满没满流,没满流就无解。
若有解,答案就是刚刚 \(t -(+\infty)> s\) 这条边用的流量,再加上删掉虚拟点相关的边(还有刚刚最后加的 \(t\) 到 \(s\) 这条边)后的 \(s\) 到 \(t\) 的最大流。
模板——有源汇上下界最小流
就是上题变成了求最小流。
就是让第二次 dinic 的源汇点分别是原图的汇点源点即可(注意顺序)。
模板——多源汇最大流
建虚拟源汇点,让虚拟源点向所有源点连一条容量为 \(+\infty\) 的边,所有汇点向虚拟汇点连一条容量为 \(+\infty\) 的边。
答案就是虚拟源点到虚拟汇点的最大流。
模型——关键边
一种暴力的做法是枚举每一条边容量加一后最大流会不会发生改变,虽然能过,但是考虑时间复杂度更低的做法。
先跑 dinic,考虑剩下的残量网络。
对于每一条边 \((u, v)\),如果从 \(S\) 到 \(u\) 和从 \(v\) 到 \(T\) 的路径上都存在流量大于等于 \(1\) 的边,那么这条边的容量就会影响到原图的最大流量。在残量网络上跑两遍 dfs 即可。
模型——最大流判定(1)
例题——[USACO05FEB] Secret Milking Machine G
最长的边最短,显然要二分。
过滤掉所有边权大于 \(mid\) 的边,然后剩下的边让它们的容量为 \(1\)。
然后跑 dinic,最大流大于等于 \(t\) 就可行。
模型——最大流判定(2)
将月球的编号 \(-1\) 转化为 \(n + 1\)。
用并查集把每条边的两点都合并,若最后 \(0\) 号点和 \(n + 1\) 号点不在同一集合中,就肯定无解。
运用分层图的思想(这回是这个了),按照天数来分层。
最开始(第 \(0\) 天)所有人都在地球 (\(0\) 号点)上,虚拟源点向 \((0, 0)\) 连一条容量为人数的边。
但是汇点不一样,无论是第几天,只要到了月球,就 over 了,所以所有的 \((i, n + 1)\) 向虚拟汇点连一条容量为 \(+\infty\) 的边(因为一个点可以同时容纳无限人)。
剩下的边就正常连,要么原地直接连到下一天,要么按照航线,当前站连到下一天的下一站。
然后天数从小到大枚举,每天连完当天的边后跑 dinic,如果最大流量大于等于要求的人数 \(k\),当前的天数就是答案。
模型——拆点(1)
开玩笑的说,这题是三分图最大匹配。
前面有最大流求解二分图,但是直接用那个方法会导致经过一头牛的流量大于 \(1\)。
考虑拆点,将一头牛拆成两个点:入点和出点。然后入点连到出点的边容量为 \(1\),这样就可以保证经过一头牛的流量小于等于 \(1\) 了。
剩下的还是按照二分图匹配那样连边。
三倍经验
模型——拆点(2)
第一问就是简单 dp。这里有一点,dp 转移本质上是图上的边。
所以我们看每个状态由之前哪个状态转移过来,然后连边。
为了防止一个点在多个不下降子序列中,向上题那样拆点即可。
如果没有状态能转移到它,就让源点向它连边。
如果当前状态等于第一问的答案,那就让它向汇点连边。
然后 dinic 跑最大流就是第二问答案。
第三问让 \(a_1\) 和 \(a_n\) 可以反复用,让和它们相关的边的容量变成 \(+\infty\) 即可。
模型——拆点(3)
因为这题题解通道还开着,所以就写在这了
实战——最大流建图
如果这个猪舍第一次打开,从源点向当前顾客连一条容量为这个猪舍起始猪的数量的边。
否则从最近一个打开这个猪舍的人向当前顾客连一条容量为 \(+\infty\) 的边。
每个顾客都向汇点连一条容量为想买的猪数量的边。
然后跑最大流即可。
最小割
模板——最小割
对于一个网络流图 \(G=(V, E)\),割定义为把原图的点划分成 \(S\) 和 \(T=V-S\) 两个集合,使得源点 \(s \in S\),汇点 \(t \in T\)。
定义割 \((S,T)\) 的容量 \(c(S,T)\) 为所有从 \(S\) 到 \(T\) 的边权总和,即
最小割就是容量最小的割。
这里直接给出结论:最小割等于最大流。下面是 OI Wiki 的证明。

应用(1)
看到这个式子很容易想到01分数规划。
二分 \(mid\),根据01分数规划,将所有边的边权减去 \(mid\)。
首先肯定要将所有边权不为正数的边直接统计进答案里,然后删掉这条边。
然后将剩下的边求最小割(浮点数 dinic,注意精度),若两者之和大于 \(0\),就缩左端点,否则缩右端点。
应用(2)
根据异或的性质,二进制的每一位是互相独立的,所以将每一位的最小值分别单独求出来。
因此如果考虑第 \(k\) 位,就是要将所有点的第 \(k\) 位分成两类,使得两类之间的边的数量最小,容易发现这就是最小割。
考虑如何建图,当 \(0\) 和 \(1\) 碰到一起的时候会产生 \(1\) 的贡献,所以先将给定的无向边容量设为 \(1\) 建出来。
根据割的定义,若某个点有初始标号,就根据它标号二进制表示的那一位来选择是源点向它连边还是它向汇点连边,容量显然要是 \(+\infty\)。
模型——最大权闭合子图
最大权闭合子图:点集内所有点的出边只能指向点集内。
可以抽象成:如果想 AK IOI,就要先 Au,AK IOI连一条边指向 Au。
难点在建图,普遍最大权闭合图的构图方式:
-
源点向所有正权点连容量为那个点权的边。
-
所有负权点向汇点连容量为那个点权的绝对值的边
-
原图的所有边容量都为 \(+\infty\)。
闭合图的权为正权点总和减去割的容量,那么割最小,闭合图的权最大。
对于本题,所有中转站向汇点连容量为它的建立成本的边,源点向所有用户群连容量为获利的边,所有用户群向需要的两个中转站连容量为 \(+\infty\) 的边。
要使净获利最大,就是让闭合图的权最大,获利总和减去最小割的容量就是答案。
模型——最大密度子图(1)
例题还是上面那道。
题解直接写到这里了。
模型——最大密度子图(2)
最大密度子图板子题,把 01 分数规划最后二分出来的答案作为 \(g\),再建图跑一遍 dinic,在参量网络上与源点连通的点就是要裁的人。
若没有点与源点连通,则任选一人裁掉即可。
模型——最小点权覆盖集
点覆盖集:满足图中每一条边所连的两点至少有一个被选的点集。
最小点权覆盖集:被选的点的点权和最小的点覆盖集。
通常求解最小点权覆盖集是 NPC 问题,但是如果图是个二分图就可以高效的求解了。
如果所有点权都为 \(1\),那么可以直接使用匈牙利算法求最大匹配,最大匹配数 \(=\) 最小覆盖数。
下面考虑使用最小割求解不保证点权都为 \(1\) 的情况。
首先看流网络能不能和原问题的解一一对应。
从源点出发的和流向汇点的边不存在容量为 \(+\infty\) 的,所以最大流肯定不是 \(+\infty\),那么最小割肯定也不是 \(+\infty\)。
因此中间那些容量为 \(+\infty\) 的边肯定不会成为割边,割边只能是跟源汇点相连的边。
如果从 \(S\) 能走到 \(T\),则说明割不合法,而且也说明了某条边的两点都没有被选进点覆盖集中,对应到原问题也不合法。
对于每一条边 \(u \to v\),要么选择 \(u^-\),要么选择 \(v^+\),以加减分成两部点。
源点向所有 \(i^+\) 连容量为 \(W_i^+\) 的边,汇点向所有 \(i^-\) 连容量为 \(W_i^-\) 的边。
因为方向上时源点到汇点,所以原图的每条边 \(a \to b\),就让 \(b^+\) 向 \(a^-\) 连容量为 \(+\infty\) 的边。
跑 dinic,然后从源点开始 dfs,标记每个点是否在残量网络与源点相连,以便判断割边。
操作次数就是割边数量,出现在点覆盖集中的点就是割边相连的点(不算源汇点)。另外那个点是源点那就说明选了 \(i^+\),是汇点那就说明选了 \(i^-\)。
模型———最大点权独立集
跟上个模型是对偶问题。
独立集:点集中的点两两没有边直接连接。
最大点权独立集:点权和最大的的独立集。
就直接按照二分图的做法写就行,以行数加列数的奇偶性分成两部点,把匹配部分换成 dinic 即可。
然后最大独立集 \(=\) 总点数 \(-\) 最大匹配数,证明去二分图找。
实战——最小割建图(1)
最好先做简化版[USACO5.4] 奶牛的电信Telecowmunication。
本题题解在这里。
实战——最小割建图(2)
发现这是个最大权闭合图,输出方案就是在残量网络上与源点连通的点就被选了。
实战——最小割建图(3)
就是求最大点权独立集,把二分图做法的匹配部分换成 dinic 即可。
费用流
模板——SPFA+EK 求最小(大)费用最大流
EK 求最大流时每次 bfs 找一条增广路,这里为了使费用最小,找的增广路需要是以费用为边权的最短路。
因为用于反悔的边的费用是原图这条边费用的相反数,所以会存在负权边,使用 spfa 求解最短路。
累积费用时要加上这条增广路的流量乘上最短路的长度。
如果要求的是最大费用,那就改为求最长路即可。
应用(1)
很容易想的一道板子题,按照题意建图即可。
源点向所有仓库连一条容量为货物数量 \(a_i\) 的边,所有商店向汇点连一条容量为需要数量 \(b_i\) 的边,代价显然都为 \(0\)。
没有限制仓库向商店运输的数量,所以所有仓库向所有商店连一条容量为 \(+\infty\) 的边,代价为 \(c_{ij}\)。
因为 \(\sum_{i=1}^m{a_i}=\sum_{j=1}^n{b_j}\),而且中间的边容量都为 \(+\infty\),所以最大流肯定是 \(\sum_{i=1}^m{a_i}\),即 \(\sum_{j=1}^n{b_j}\)。
然后用 SPFA+EK 分别求出最小费用最大流和最大费用最大流即可,具体区别就是最小费用是在 SPFA 中求最短路,最大费用是最长路。
应用(2)
也是比较显然的一道题(这题也可以用数学+贪心做,就是糖果传递多倍经验)。
多的要给少的点补,那么源点向多的连容量为多出来数量的边,少的向汇点连容量为缺少数量的边。
多出来的与缺少的正好互补,所以最大流是固定不变的。
每个点向左右两点连费用为 \(1\) 的边,然后求最小费用最大流即可。
二分图最优匹配
在建图上还是基本按照最大流部分的二分图匹配方式来(也就是建边和容量不变)。
在此基础上只需要将左部点 \(i\) 连向右部点 \(j\) 的边的费用设为 \(c_{ij}\) 即可。
然后分别跑一遍最小费用最大流和最大费用最大流。
最大权不相交路径
一共有三问,注意这三问的路径起始点都不能相同,所以源点向 \(m\) 个起点连的边的容量应该为 \(1\)。
第一问不能重点也不能重边,不能重点可以用拆点的技巧,入点向出点连一条容量为 \(1\),费用为这个点的权的边,这样到达这个点的路径最多只会有一条。
第二问可以重点,那么就不用拆点了,终点向汇点连的边的容量要改为 \(m\)。
第三问也可以重边,在第二问基础上把每个点向下连的边的容量改为 \(m\) 即可。
对这三问分别建图跑最大费用最大流。
网格图模型(1)
每个点上的数最多只能被取一次,容易想到将点转边,拆成入点和出点。
然后入点向出点连一条容量为 \(1\),费用为 \(a_{ij}\) 的边。
但是经过一个点后仍可以再次经过,只是不会再取到数了,所以入点再向出点连一条容量为 \(k-1\),费用为 \(0\) 的边。
然后按照拆点的形式,这个点的出点向下面和右面的入点连一条容量为 \(k\),费用为 \(0\) 的边。
要让取得数之和最大,所以求一下最大费用最大流即可。
网格图模型(2)
显然的多源汇最大费用最大流板子题。
只有第一次经过才能采集这一点跟上面使用同一个建图方式。
多源汇使用最大流部分多源汇最大流的思路建图。
最后跑最大费用最大流就结束了。
拆点
这题思路不是很显然,但是很简单巧妙。
因为有的干净餐巾数量与当天要用的数量要相等,所以每天晚上剩下的全都是脏餐巾。
这引导我们可以将每天拆成早上的干净餐巾和晚上的脏餐巾两个点。
首先,每天早上都要准备 \(r_i\) 块餐巾用,将每天早上向汇点连一条容量为 \(r_i\),费用为 \(0\) 的边。
连到汇点的边可以抽象成是用掉了干净餐巾。
然后每天晚上都会有 \(r_i\) 块脏餐巾,源点向每天晚上连一条容量为 \(r_i\),费用为 \(0\),的边。
源点连出来的边可以抽象成是餐巾,具体是干净的还是脏的要看是连到了早上还是晚上。
每天早上都可以买任意块餐巾,源点向每天早上连一条容量为 \(+\infty\),费用为 \(p\) 的边。
每天晚上的脏餐巾可以让其中任意块不洗,延续到下一天,所以每天晚上(除最后一天)向下一天晚上连一条容量为 \(+\infty\),费用为 \(0\) 的边。
每天晚上的脏餐巾还可以送去快洗店和慢洗店去洗,每天晚上向 \(m\) 天后和 \(n\) 天后的早上分别连一条容量为 \(+\infty\),费用为 \(f\) 和 \(s\) 的边。注意如果洗干净后已经过了最后一天,就不用连边了。
最后跑最小费用最大流即可。
上下界可行流
由于是每天至少要 \(a_i\) 人,所有当天的流量是可以大于 \(a_i\) 的,相当于每条边有下界限制。
然后本题要求最小花费,也就是要求无源汇上下界最小花费可行流。
考虑如何将志愿者在图中表示出来,设有志愿者 \(i\) 能从第 \(2\) 天工作到第 \(3\) 天,那么就相当于从 \(2\) 开始往后流两天,到 \(4\) 停止。
为了保证流量守恒,让 \(4\) 向 \(2\) 连一条容量为 \(+\infty\),费用为 \(c_i\) 的边。
类似无源汇上下界可行流的做法,按照 \(a_i\) 和 \(a_{i-1}\) 的大小关系来判断是源点向它连边(小于)还是它向汇点连边(大于)。
第 \(i\) 天的无偿延续到第 \(i+1\) 天,连一条容量为 \(\infty - a_i\),费用为 \(0\) 的边。
最后求最小费用最大流即可。

浙公网安备 33010602011771号