网络流学习笔记

#0 一些定义

对于带权有向图 GG

  • 定义 (xy,w)(x\to y,w) 为一条权值为 ww 的从 xx 连向 yy 的有向边;
  • 定义 V(G)V(G) 为图 GG 的点集,E(G)E(G)GG 的边集;

#1 什么是网络流

定义 1.1(网络、源点汇点、容量)

定义网络 (G,S,T)(G,S,T) 如下:

  • GG 是一个边带非负权有向图SSTTGG 中的节点;
  • SS 被称为源点TT 被称为汇点
  • 对于 GG 中的边 (xy,w)(x\to y,w)ww 被称为改边的容量
定义 1.2(流、流量)

对于一个网络 (G,S,T)(G,S,T),定义一组 F=f[1,E(G)]F=f_{[1,|E(G)|]},其中 fif_i 为第 ii 条边被使用的流量,需要满足:

  • 1iE(G)\forall 1\le i\le|E(G)|,有 fiwif_i\le w_i
  • uV(G),u{S,T}\forall u\in V(G),u\notin\{S,T\},有 (xiyi,wi)E(G),yi=ufi=(xiyi,wi)E(G),xi=ufi\sum\limits_{(x_i\to y_i,w_i)\in E(G),y_i=u} f_i=\sum\limits_{(x_i\to y_i,w_i)\in E(G),x_i=u} f_i,即流入该点的流量和等于流出该点的流量和;

定义流 FF流量 F|F|(xiyi,wi)E(G),yi=Tfi\sum\limits_{(x_i\to y_i,w_i)\in E(G),y_i=T} f_i,即流到汇点 TT 的流量和。

形象地:

对于一个带权有向图 GG 及图上的两个点 SS(源点)和 TT(汇点),可以这样看待这张有向图:

  • 将点看作水池,边看作水管,则有向边 (xy,w)(x\to y,w) 表示单位时间内最多 ww 单位的水可以通过该水管从水池 xx 流向水池 yy
  • 源点 SS 是水源,单位时间内能提供无限单位的水;
  • 汇点 TT 是排水口,单位时间内能接受无限单位的水;

流就是让水从水源流到排水口而不爆水管的方案,流量就是方案中从排水口排走的水量。

#2 网络最大流

定义 2.1(有源汇最大流)

对于一个网络 (G,S,T)(G,S,T),定义其上的最大流 MaxFlow(G,S,T)\text{MaxFlow}(G,S,T)一组满足 F|F| 最大的流 FF

严格的定义理应是满足 F|F| 最大的 FF 构成的集合,简便起见就不这样记了。

例如对于这个网络:

其一组最大流(流量为 99)如下:(边权为该边使用的流量)

2.1 最大流的求解

2.1.1 反向边的引入

考虑贪心,不断找到从 SSTT 且容量均 >0>0 的路径(增广路),即通过这条路径可以让至少 11 的流量从 SS 流到 TT,那么将答案增加 11,并将路径上边的容量全部减少 11

定义 2.1.1(增广路)

定义一条增广路为一条从 SSTT 路径,满足路径上边的容量均 >0>0

定义 2.1.2 pre(增广)

对于一条增广路 pp,定义增广操作:

  • w(p)=min(xiyi,wi)p{wi}w(p)=\min\limits_{(x_i\to y_i,w_i)\in p}\{w_i\},即 pp 中的边的最小容量;
  • pp 中边的容量均减少 w(p)w(p)
  • 令答案(最大流大小)增加 w(p)w(p)

那么上述贪心实际上就是不断找增广路进行增广的过程。

但这个贪心是错的,对于一个这样的最大流大小为 22 的网络:

贪心会求出这样的最大流,大小为 11

因此参考反悔贪心,引入反悔(退流)思想。

具体的,对于每条边 (xy,w)(x\to y,w),建立反向边 (yx,0)(y\to x,0)

并修改增广的定义:

定义 2.1.2(增广)

对于一条增广路 pp,定义增广操作:

  • w(p)=min(xiyi,wi)p{wi}w(p)=\min\limits_{(x_i\to y_i,w_i)\in p}\{w_i\},即 pp 中的边的最小容量;
  • pp 中边的容量均减少 w(p)w(p)pp 中边对应的反向边的容量均增加 w(p)w(p)
  • 令答案(最大流大小)增加 w(p)w(p)

例如对于例子中的网络,进行一轮新的增广后是这样的:

不难发现现在网络中还存在增广路,还能进行增广操作:

那么修正后的贪心就成功求出了一组最大流,最终每条边反向边的容量即为这组最大流中该边使用的流量

这其实是一个反悔贪心,增广过程中走反向边相当于退流。

容易证明,不断重复上述操作一定能求出一组最大流。

2.1.2 EK 算法

直接模拟上述过程,可以得出求解网络流的朴素算法。

建完图后,用 bfs 来找增广路,并记录下每个点的前驱节点(令其入队的点)以便找到增广路上的边。如果 bfs 时访问到了汇点,那么就找到了一条增广路。

为了储存反向边,不妨令边的编号从 22 开始,并令编号为 ii 的边和编号为 i1i\oplus 1 的边互为反向边。这样实现起来会方便很多。

不断跑 bfs 找增广路去进行增广操作直到 SSTT 不连通,即可求解出网络的一组最大流。该算法被称作 EK 算法,时间复杂度是 O((n+m)V)O((n+m)V) 的,其中 VV 是最大流的大小,即值域。

2.1.3 dinic 算法

dinic 算法是 EK 算法的一种改进,其可以同时增广多条增广路。

其大体流程如下:

  • 每次增广前先跑一次 bfs,将节点按照距离 SS 的最短路(边数)depudep_u 分层,增广时要求增广路相邻点的 depudep_u 恰好相差 11
  • 接下来跑 dfs,对所有满足条件的增广路同时进行增广操作;
  • 不断重复上两步直至 bfs 时 SS 不能到达 TT,即不存在增广路;

伪代码如下:

int dfs(int u,int w) // u 表示当前节点的编号,w 表示流向当前点的流量,返回值为成功增广的流量
{
    int sum=0;
	遍历所有从 u 出发的边 i
	{
		int v=i 到达的节点;
		if(dep[v]==dep[u]+1)
		{
			int re=dfs(v,min(w,i 的容量));
			if(re!=0)
			{
				更新正向边和反向边的容量;
                w-=re;
				sum+=re;
			}
		}
	}
	return sum;
}
int dinic()
{
    int res=0;
    while(bfs 可以到达 汇点) res+=dfs(s,inf);
    return res;
}

此外,dinic 算法还有几个优化,分别是当前弧优化断层优化

  • 当前弧优化:把已经“榨干”(容量为 00)的边删掉;
  • 断层优化:把已经“榨干”的节点“删掉”(return 时若 sum==0 则令 dep[u]=0);

一般默认会加上这两个优化。

dinic 的最劣复杂度为 O(n2m)O(n^2m),证明考虑每次找到的增广路边数一定最小,故 depTdep_T 递增;而单次 dfs 中每条增广路都会删掉至少一条边(当前弧优化)故最多有 mm 条增广路,而每条增广路长度至多为 nn

并且 dinic 在容量均为 11 的网络上的复杂度为 O(mn)O(m\sqrt n),具体证明及更多关于复杂度的信息可以看Dinic的几种复杂度 - myee - 博客园

但由于网络流题目的图一般都有特殊性质,故加上优化后的 dinic 常常十分快速,基本不会被卡,在 OI 中最常用。

求解网络流还有 ISAP 和 HLPP,由于太过复杂且 OI 中不考,故不再介绍。

#3 最小费用最大流

在最小费用最大流问题中,每条边新增了一个单位代价 wiw_i,表示这条边流过 11 流量需要 wiw_i 的代价,需要求解一组总代价最小的最大流。

也可以使用 dinic 算法解决,每次增广单位代价最小的增广路即可,正确性显然。建边时反向边代价为 wi-w_i,而且 bfs 需要换成 spfa,且增广时要求增广路相邻点的 depudep_u 恰好相差它们之间的边的代价。

值得注意的是,由于代价可能为 00,所以 dfs 时需要防止重复访问点。

#4 有向图最小割

定义 4.1(有向图的割)

对于一个带权有向图 GG 及图上的两个点 SSTT,定义一组 SSTT 的割 CC 如下:

  • CE(G)C\in E(G)
  • 删掉 CC 中的边后 SS 不能到达 TT

定义割 CC 的大小 C|C|(xiyi,wi)Cwi\sum\limits_{(x_i\to y_i,w_i)\in C} w_i,即割中的边权和。

定义 4.2(有向图的最小割)

对于一个带权有向图 GG 及图上的两个点 SSTT,定义其最小割 MinCut(G,S,T)\text{MinCut}(G,S,T)C|C| 最小的一组割 CC

定理 4.1(最大流等于最小割)

对于一个带权有向图 GG 及图上的两个点 SSTT,有:

MaxFlow(G,S,T)=MinCut(G,S,T)|\text{MaxFlow}(G,S,T)|=|\text{MinCut}(G,S,T)|

证明很简单,首先由于跑完最大流算法后 SS 不能到达 TT,故 MaxFlow(G,S,T)MinCut(G,S,T)|\text{MaxFlow}(G,S,T)|\ge|\text{MinCut}(G,S,T)|,又显然 MaxFlow(G,S,T)MinCut(G,S,T)|\text{MaxFlow}(G,S,T)|\le |\text{MinCut}(G,S,T)|(最大流中满流的边集构成割),故得证。

更详细的证明可以自行上网寻找。

并且根据一组最大流 FF,可以简单地构造出最小割:

  • 令每条边的剩余容量 cic_iwifiw_i-f_i
  • SS 出发,只经过 ci>0c_i>0 的边,求出能到达的点集 AA
  • 一组最小割 CC 即为 {(xy,w)E(G)xA,y(V(G)A)}\{(x\to y,w)\in E(G)|x\in A,y\in (V(G)-A)\},即一端在 AA 中的边的集合;

#5 有上下界网络流

5.1 有上下界循环可行流

nn 个点,mm 条有向边,每条边有一个流量下界 lil_i 和流量上界 rir_i,求该图的一个循环可行流,即你需要构造 mm 个变量 fif_i 满足:

  • 流量合法:i,lifiri\forall i,l_i\le f_i\le r_i
  • 流量平衡:对于每个点 ii,设 IiI_iii 的入边集合,UiU_iii 的出边集合,那么有:jIifj=jUifj\sum\limits_{j\in I_i} f_j=\sum\limits_{j\in U_i}f_j

考虑每条边先钦定流过 lil_i 的流量,转化为每条边只有流量上界 rilir_i-l_i,那么流量有可能不平衡。

考虑用点与源点 SS 或汇点 TT 的连边刻画每个点的额外流量。记 bi=jIiljjUiljb_i=\sum\limits_{j\in I_i} l_j-\sum\limits_{j\in U_i}l_j,即每个点进入的流量减出去的流量,那么:

  • bi<0b_i<0,连接边 (iT,bi)(i\to T,-b_i)
  • bi>0b_i>0,连接边 (Si,bi)(S\to i,b_i)

原先的边 ii 直接连 (xiyi,rili)(x_i\to y_i,r_i-l_i)

跑最大流,若所有 (iT,bi)(i\to T,-b_i)(Si,bi)(S\to i,b_i) 均满流,则有解,否则无解。设跑完后第 ii 条边的流量为 cic_i,则第 ii 条边的流量为 li+cil_i+c_i

时间复杂度和最大流相同。

5.2 有上下界有源汇最大流

考虑将其转化为循环流来消去上下界,设源点为 ss,汇点为 tt,则不难发现相当于从 ss 出发的流量等于到达 tt 的流量,连边 (ts,[0,])(t\to s,[0,\infin])

跑出循环流后,记 resres(ts,[0,])(t\to s,[0,\infin]) 的。接下来把 (ts,[0,])(t\to s,[0,\infin]) 去掉,设第 ii 条边在循环流中流过了 cic_i 流量,则连接边 (xiyi)(x_i\to y_i) 时:

  • 正向边流量为 ricir_i-c_i
  • 反向边流量为 cilic_i-l_i

则答案即为 resres 加上当前图从 sstt 的最大流。

5.3 有上下界有源汇最小流

最大流是尽可能从 ss 流到 tt,那么最小流就是尽可能从 tt 退回 ss

跑出 ttss 的最大流 rfrf,答案即为 resrfres-rf

#6 常见模型&技巧

网络流的常见建模