目录



网络流

基础知识

定义

一张 网络:由一个图和一个函数 \(C: E\to \R\) 组成。\(C(e)\) 表示一条边 \(e\) 的 容量

其中有两个关键的点,称为 源点(S)汇点(T)

下面用 \(C(x,y)\) 表示 \(x,y\) 这条边的 \(C\) 函数值。对于其它定义域为边集的函数都写成这样。

一个合法的 :考虑这样的一个函数 \(f:E\to \R\),满足:

  • 流量平衡: \(\forall u\neq S,T,\sum\limits_{v\to u} f(v,u)=\sum\limits_{u\to v} f(u,v)\)
  • 斜对称性:\(\forall u\to v, f(u,v)=-f(v,u)\)
  • 流量限制:\(\forall u\to v, f(u,v)\le C(u,v)\)

可以推得:

  • \(\sum\limits_{S\to u} f(S,u)=\sum\limits_{u\to T} f(u,T)\)

把这个值称为这个流的 流量

有一个形象的理解是,\(S\) 为自来水厂,\(T\) 是你家,每条边相当于是一个水管,带一个容量限制。然后中间有若干的中转站(并不能提供水)。流量就是流到你家里水的数量。

一类问题是,最大化流量。 称为 最大流问题

一类问题是,有一个费用函数 \(w:E\to \R\),表示每条边每个单位的流量所需要付出的费用。在最大化流量的前提下,最小化代价。称为 最小费用最大流 问题。

此外,还有一类问题叫做 “最小割”。严格定义如下:

  • 将点集 \(V\) 划分成 \(A,B\) 两个集合(即,\(A\cap B=\empty, A\cup B=V\)),使得 \(S\in A,T\in B\)
  • 定义割的大小为:\(\sum\limits_{u\in A,v\in B} c(u,v)\)。最小化这个东西。

换句话说,就是:切掉一些边使得 \(S,T\) 不连通,最小化切掉的边的边权和。

【定理】最大流=最小割

证明:首先显然每个流都$\le $每个割。然后做完最大流的时候,在残量网络上,把 \(S\) 能到的那些点设为 \(A\) 集,剩下的设为 \(B\) 集,\(A,B\) 就构成一个最小割,且这个割的大小就等于最大流。

最大流 - Dinic算法

首先我们发现,“流”这个东西其实就是很多条从 \(S\to T\) 的路径,每条路径流了一个流量,叠加起来,然后给反向边放一个负权就行了。总流量就是每条路径流的流量和。

注意到,“流”和“\(S\to T\) 路径”都满足除了 \(S,T\) 外的点流量守恒。

那假设咱现在有一个流。它是最大流吗?怎么把它变更大?

有一个显然的想法是我再找一条路径,叠加上去。如果流完之后还能满足流量限制,那我们就可以把流量加上这条路径流的量。

这玩意可以转化为:每次流完之后,我们把一条边的容量设为它还剩下的流量 \(C'(u,v)=C(u,v)-f(u,v)\)。这样得到的网络称为 残量网络。同时,这条路径被称为 增广路

反之,如果我们找不到增广路,那就说明当前是最大流了。

那如果我们有一次流错了,导致我找不到增广路,但还不是最大流?有没有这种情况?

注意到我们每条边有一个负权的反边。那如果 \(u\to v\) 流了一个流量 \(f\),那么残量网络上 \(C'(v,u)=0-(-f)=f\)。(注意到 \(C\) 的限制是针对 \(u\to v\) 的,\(v\to u\) 容量限制为 \(0\),如果没有另外加边的话)。如果我们有一个增广路流了这一条边,就相当于把这次 \(u\to v\) 的流 反悔 了。

这个机制确实保证了正确性,但是复杂度与容量有关。如果容量 \(1e9\) 直接挂了。

Dinic算法就解决了这样一个问题:我每次先BFS把图分层,每次只从上一层流到下一层。

然后它就跑的飞快了。

板子

​Dinic: 代码​

一些易错点:

  • BFS中, 不要忘记返回值,以及dep和Q的清空;对于当前弧优化,记得把当前弧搞出来
  • DFS也不要忘记返回值
  • 网络流别忘记clear
  • 网络流中不要忘记定义变量 S,T
  • ​DFS(S,INF)​
  • ​F(i,1,tot) dis[i]=INF;​
  • 最大费用最大流,你应该判 ​​dis[T]<=-INF​​ 的时候 break,而不是 ​​dis[T]<0​

通常,网络流题目并不难在写出来网络流(除非出题人卡常,要写一些比dinic快的网络流),而是把模型建出来。

最大权闭合子图模型

一个有向图中,每个点有一个权值,可能正可能负。选一个点,就要选这个点所有后继。请选择若干个点,满足条件,且权值和最大。

这就是 “最大权闭合子图” 模型。

可以采用最小割建图来解决这个问题。有两种常见的思想:割一条边代表选,以及,割一条边代表不选。

分析一波。对于这个问题,我们考虑:假设没有任何限制,那肯定就把所有正的全都选上。

但是我们选了某些正的点,可能相应的就要选一些负的点。

对 “选...就要选...” 进行一个联想:在一张图上,要把 \(S,T\)​ 割开,如果我要保留一些边,就必须割掉另外一些边。也就是 “保留...就要割...” 的模型。

对于每个正权的点,要选它,就要选另外一些点。把权值放在边上,就变成 “选...就要选...” 模型。

根据上面的联想,我们令正权点连的边, “保留” 为 “选”。那它后继应该有一些能到 \(T\) 的边,使得它能构成 “保留...就要割...” 模型。

而且后面这些边,应该反过来,是以 “割” 为 “选”。

注意到,“保留”为“选”,相当于“割”为不选。那我们先全选,减掉割,就行了。

后面以“割”为“选”,我们考虑能不能保持同步,也是用 “全选,减掉割” 的办法处理。

啪的动一下脑子:对于负权点 \(-a\),我们连一条权值为 \(a\) 的边。如果割掉,就表示选了这个负权点。好对啊!!!

那我们现在已经有了 “全选,减掉割” 这个idea了,并且,我们要把点权放在边上。

那我们具体怎么连边呢?考虑 “保留...就要割...” 模型,总体上大概是前者离 \(S\) 近,后者离 \(T\) 近。

那我们干脆把前者连到 \(S\),后者连到 \(T\)。也就是说,把正权接在 \(S\) 上,负权接在 \(T\) 上。对于 “选...就要选...”,直接连边就行。

仔细一想,这样好像就对了!

总结一下:

  • 对于正权点 \(u\),权值为 \(a_i\),我们连边 \(S\xrightarrow[]{a_i} u\)
  • 对于负权点 \(u\),权值为 \(-a_i\),我们连边 \(u\xrightarrow[]{a_i} T\)
  • 对于原图上的 \(u\to v\),连边 \(u\xrightarrow[]{\infin} v\)
  • 正权和 - 最小割,为答案

这是一个常见模型。有很多延伸应用。

板子:洛谷 3410, [网络流24题]太空飞行计划问题

最大权导出子图 / 最大密度子图(poj 3155)

最大权导出子图,很好理解。就是点和边都带权,找一个导出子图,使得权值和最大。

考虑导出子图这个事情:如果选了一个边,就要选它两边的点。

把边建出来点,那它就变成了最大权闭合子图。

定义一个导出子图的密度为:边数/点数。找到最大密度的子图。

考虑二分。假设密度能 \(> \rho\),设边数、点数为 \(E,V\),则 \(\dfrac{E}{V}> \rho\)。

也就是说 \(E-\rho V> 0\)。设边权为 \(1\),点权为 \(-\rho\),找到最大权导出子图,看看是否 \(>0\)。

(注意要严格大于,因为如果是 \(\ge0\),你不选也 \(\ge 0\),没法限制)

然后卡卡精度就行了。

经典in-out拆点

对于一个点 \(u\),拆成 \(u_1,u_2\),\(u_1\to u_2\) 连一条边,边权为 \(u\) 的点权。

这样就可以把点转换成边,用边来做点。

SCOI2007 蜥蜴

我们发现这个东西很像一个最大流。但是限制不在边上,而是点:每个柱子有被经过次数的限制 \(h(i,j)\)。

对于 \((i,j)\) 位置的点,我们把它in-out拆点,然后中间那条边权设为 \(h(i,j)\),即可解决这个限制。

对于原图上 \(u\to v\) 的边,连边 \(u_2\to v_1\) 即可。

洛谷1345

这题是一个“最小点割”:每个点带点权,割若干个点使得 \(c_1,c_2\) 不连通。最小化割掉点的权值。

令 \(c_1,c_2\) 为 \(S,T\)。我们把点in-out拆点后,把中间那个边权设为该点的点权。\(u_2\to v_1\) 的边权设为inf。

求这个图的最小割,显然只会割在点中间那个边上。我们发现它就和原图割点的方案一一对应。求这个最小割就行了。

三元匹配

有三类点,\(A,B,C\)。边只会是 \(A\to B,B\to C\) 类型的。

对于一个 \(a,b,c\),满足:

  • \(a\in A,b\in B,c\in C\)
  • \(a\to b\),\(b\to c\) 之间,都有边

称 \((a,b,c)\) 是一组三元匹配。一个点只能被匹配一次。请你选出最多的三元匹配。

(相当于把二分图匹配扩展一下)

根据二分图匹配的套路,对于 \(a\in A,c\in C\),我们连边 \(S\to a,b\to T\),容量为 \(1\),就可以限制 \(a,c\) 只被选一次。

但是 \(b\) 可能被选很多次,那咋办?我们不能靠 \(S,T\) 了,靠自己!我们自己裂开来,in-out拆点,中间连一条边,容量为 \(1\)。这样就可以限制它只被选一次了。

然后跑最大流即可。

这个技巧也可以解决更多元的匹配。

例题如:洛谷1402,1231 (其实算是双倍经验了)

环覆盖

对于一个有向图,“环覆盖”可以看成是入度与出度的匹配。

然后就直接连边做就行了,注意一个点可以匹配很多次,所以连到 \(S/T\) 的边容量为它的入/出度。

例题如 CF510E,也没见到别的。

多元选择拆点

有 \(n\) 个东西,都有很多个选项。我们可以把每一个选项拆点,解决选项之间的限制问题。

它可以结合距离限制模型,解决更多问题。

SCOI2007 修车

对于一个师傅,它可能会修 \(1,2,3...n\) 辆车。我们把这 \(n\) 种选择全都拆成点。

注意到它依次修 \(k\) 辆车,时间为 \(a_1,a_2...a_k\),总时间为 \(a_k+2a_{k-1}+3a_{k-2}...+ka_1\)。越靠后,系数越小。换句话说,系数是 “倒数第几个”。

那我们这么拆:对于这个师傅 \(x\),拆点 \(x_1,x_2..x_n\) 表示:修倒数第 \(1\),倒数第 \(2\)....倒数第 \(n\) 辆车。

那给它一个 \(1,2,3...n\) 的费用就行了。容量为 \(1\),表示修一辆车。

\(S\) 先连到一个 \(S'\),容量为 \(n\),表示必须修 \(n\) 辆车。然后 \(S'\) 连到每个师傅。每个车再连到 \(T\),容量为 \(1\),表示每辆车恰好被修一次。

以下是某个同学代笔写的,与文章内容无关,但文笔太好了,让我们来欣赏一下。

zps会站在这些车前面,他认为这些车修的不好,于是一个个教育这些师傅,朝问道,夕死可矣,在修车的时候没有注入灵魂,看不出人的傲骨,车乃大丈夫行天下之利器,辙及天南海北,反映的是一个人的精神和风骨,君子者,为天地立心,为生民立命,为往圣继绝学,为万世开太平,一个合格的修车夫,应饱含理想主义的信仰,做一名斯多葛派的信奉者,不灭人生而有之的飒踏和洒脱,在尘世面前,保持初心,而不是庸于俗世泯然芸生,心之所行,万物随之奔流,修车乃格物之道,正如王阳明的心学理念,需人神之悟道,皆物我合一。我们看穿车之本质,其属性为两个标量,在一维不可逆的时间体系逻辑中,我们需以zps的自我基础元的意识世界为第一视角,从计算的本质——映射开始,领悟绝世大儒对此题的理解和悟道,总而言之,AprilGrimoire txdy !!!!!!1

距离限制模型

每个东西有若干个选项,第 \(i\) 个东西的选项有编号 \(j\in [1,m]\),并且有权值 \(v_{i,j}\)。首先我们用上一个模型,把这些选项拆点。

拆完点之后,东西之间有选项的限制。设 \(i\) 这个人选的选项是 \(c_i\)。有若干个限制,每个形如:\(c_i-c_j\le/\ge k\)。我们需要满足每个限制,然后使权值和最小或最大。

这个就是距离限制模型。

它有一些延伸,比如说我们要做 \(|c_i-c_j|\le k\),就可以转化成 \(c_i-c_j\le k\) 且 \(c_j-c_i\le k\),再套用这个模型。

那它怎么做呢?

其实我们并不是把选项拆“点”,而是拆“边”。对于每个选项,我们拆出 \(m+1\) 个点,连成一条链。中间 \(m\) 条边的权值是每个选项的权值。\(S\) 到最开始的点,最后的点到 \(T\) ,有两条 \(\infin\) 的边。然后我们认为 “割” 为 “选”。

形式化地,对于第 \(i\) 个东西,我们设它拆成的点是 \(A_{i,1}...A_{i,m+1}\)。

如下建图:


\[\forall i\in[1,n],\\\begin{cases} S\xrightarrow[\infin]{} A_{i,1}\\ T\xrightarrow[\infin]{} A_{i,m+1}\\ \forall j\in[1,m],\ A_{i,j}\xrightarrow[]{v_{i,j}} A_{i,j+1} \end{cases} \]


称这玩意为东西 \(i\) 的 “选项链”。 我们把每个东西的 “选项链” 都建出来。

这是一个经典的建图,它有什么性质?

我们在链之间横向插边!

比如我们在 \(A_{u,i}\) 和 \(A_{v,j}\) 之间连一条 \(\infin\) 边。那么,如果 \(v\) 选在 \(j\) 之前,\(u\) 就要选在 \(i\) 之前。否则就会有 \(S\to A_{u,i}\to A_{v,j}\to T\) 这样一条通路。

对于一个 \(d\),考虑:\(\forall i\),连边 \(A_{u,i}\to A_{v,i+d}\) 。如果 \(v\) 选在了 \(i\),则 \(u\) 必须选在 \(i-d\) 之前。这就实现了 \(c_v-c_u\ge d\) 。

然后求最小割就是最小的权值和。

那么问题来了,如何求最大的权值和?

首先我们搞一个够大的数 \(M\),然后把每个 \(v_{i,j}\) 变成 \(M-v_{i,j}\)。然后用 \(M\times n\) 减去最小割即可。

感性理解:假设默认可以得到 \(M\) 的收益,考虑每个选项 损失 的收益。让损失最小,就等价于权值和最大了。

直接应用:HNOI2013 切糕,codechef RIN

行列建点

适用于网格图的建图技巧。

把每行、每列都看成点,把位置 \((x,y)\) 看成行与列的关系,建边。

一般题目难都难在想到这样建,以及处理关系呢。

大概就是这样的套路吧(笑)。

bzoj4883

把每个格子看做行与列连了一条边,然后要定向。指向的那个点就是这个格子控制的点。

那我们一共要选 \(n+m\) 个边和点。那很显然就是一个基环树森林。

用并查集就可以类似kruskal贪心的做了。

虽然不是网络流题,但可以根据这个题,熟悉一下行列建点是怎么建的。

洛谷4311

逆向思维。假设一开始每个位置都有士兵,最少删掉多少个士兵?

删掉一个士兵,相当于把它对应的行和列都减少了 \(1\) 的贡献,答案也可可以变小 \(1\)。这就像我们把这里原来的一条流给取消了,三条边的流量都 \(-1\) 且答案 \(-1\)。

我们把 \(S\) 连到行点,列点连到 \(T\)。对于中间的一个格子,连接它对应的行列点,容量为 \(1\)。

对于每行每列,我们把它连到 \(S,T\) 的边的容量,设置为它最多能删掉多少个士兵使得它能满足条件。

显然这张图的最大流就是最少删除的士兵数,拿总数减掉就行。

HEOI/TJOI2016 游戏

假设没有石头,答案就是 \(\min(n,m)\)。

我们可以把它看成是行点与列点的匹配。每个可以放炸弹的格子相当于连接了一个行点和一个列点,答案就是最大匹配。

这样就可以做仅有软石头的情况了。考虑硬石头咋做。

现在放了一个硬石头,考虑它的那一行(列同理),如果我们在左边和右边放了一个炸弹,我们发现没事。因为这个硬石头把它们隔开了。

那其实我们以为这是 “一行”,但其实并不是 “一行”,它相当于是裂开了,变成两 “小行”。在两个“小行”内部不能同时有炸弹,但是在“小行”之间没有限制。

因此我们可以灵活运用这个拆点,把行再拆开。给这些拆开来的行赋予一个新的编号。

同理,我们给拆开来的列也分配新的编号。

然后就对这些 “新行”,“新列” 做最大匹配就行了。

SCOI2015 小凸玩矩阵

每行每列选一个 $\to $ 行列匹配,直接行列建点。

那第 \(k\) 大怎么做?

先二分,设第 \(k\) 大能 \(\ge mid\),那么 \(\ge mid\) 的设为 \(1\),\(<mid\) 的设为 \(0\)。我们得选出 \(k\) 个以下的 \(1\)。

\(1\) 的个数其实就是行列的最大匹配数。然后就每次跑一遍二分图匹配,然后check一下就行了。