关于去除《算法导论》中介绍的前置重贴标签算法的反平行边限制的方法、实现细节与正确性证明
Piwry
·
2025-10-22 18:31:18
·
算法·理论
前言
一直没自己实现过最大流,出于偏好写的第一个最大流算法选用了《算法导论》的“前置重贴标签算法”,并且也采用了《算法导论》给出的实现方式。但是《算法导论》中对流网络的定义要求不能有反平行边(即同时存在 (u, v), (v, u) );虽然存在对复杂度影响为常数级别的转换为等价图的方法(把每个结点 u 复制一份 u' ,然后每条边 (u, v) 转化成 (u, v'), (v', v) 两条边即可),可偏偏就是这个常数让我过不了最大流加强版的板子...
于是基于《算法导论》的原始算法和证明,自己又研究了一下,成功魔改出一个不增加常数直接兼容反平行边的做法,并且稍微整理了下实现细节和需要对《算法导论》原复杂度/正确性证明打补丁的地方,发布于此,希望能帮助同样照着《算法导论》写的有此疑惑的人 qaq。
可能思路会有些笨拙,也可能会有因疏忽而不严谨的地方,抛砖引玉,还望读者海涵 QAQ(叠甲)。
(另注:以下具体采用的参考书为《算法导论》第三版。本文非常需要配合原书籍阅读!本文完全采用原书记号;出于篇(省)幅(事)原因,原书已有的定义和引理会被直接略去。)
(注2:如果需要,可以从 这里(提取码 rdki;解压码 PiwryFlow)获取本文所涉及的原书章节。)
对定义的修改
列表中“(p...)”指的是该定义对应的原书页码。
注意到,核心在于对流 f 的容量限制的定义的修改,以及对流 f 添加的新限制 f(u, v)=-f(v, u) ;这相当于将 v 到 u 的反向流直接“体现在” f(u, v) 中了。至于其余定义的修改只需基于此照葫芦画瓢地更改,使其含义(模型?)契合原定义即可。
(p414)我们允许流网络 G=(V, E) 出现反平行边,即 (u, v), (v, u) 可以同时被包含于 E 中;但是仍然不允许重边。
(p415)我们修改流 f:V\times V \rarr R 定义中的容量限制 为:对于所有的结点 u, v\in V ,要求 -c(v, u)\leq f(u, v)\leq c(u, v) 。
(p415)我们为更改流 f 的一个限制,这个限制原本是:当 (u, v)\not\in E 时,f(u, v)=0 。我们更改为:当 (u, v)\not\in E 且 (v, u)\not\in E 时,f(u, v)=0 。
(p415)我们为流 f 另添加一个限制:对于所有的结点 u, v\in V ,要求满足 f(u, v)=-f(v, u) 。
(p415)我们修改流 f 定义中的流量守恒 为:对于所有的结点 u\in V-\{s, t\} ,要求 \sum\limits_{v\in V}f(u, v)=0=\sum\limits_{v\in V}f(v, u) 。
(p415)我们修改流值 |f| 的定义为:\begin{aligned} &|f|=\sum\limits_{v\in V}f(s, v) &(26.1)\end{aligned} 。
(p419)我们修改残存容量 c_f(u, v) 定义为:\begin{aligned} &c_f(u, v)=\left\{\begin{aligned}&c(u, v)-f(u, v) &\text{if } (u, v)\in E \text{, or } (v, u)\in E \\ &0 &\text{Otherwise} \end{aligned}\right. &(26.2)\end{aligned} (且可以注意到,如此修改后,对任意 u, v\in V ,仍然满足 c_f(u, v)\geq 0 )。
(p419)我们修改流 f' 对流 f 的递增 f \uarr f' 的定义为:\begin{aligned} &(f\uarr f')(u, v)=\left\{\begin{aligned}&f(u, v)+f'(u, v) &\text{if } (u, v)\in E \text{, or } (v, u)\in E \\ &0 &\text{Otherwise} \end{aligned}\right. &(26.4)\end{aligned} 。
(p422)我们修改横跨切割 S, T 的净流量 f(S, T) 的定义为:\begin{aligned}&f(S, T) =\sum\limits_{u\in S}\sum\limits_{v\in T}f(u, v) &(26.9)\end{aligned} 。
(p431)我们修改预流 f 的(弱化了的)流量守恒 性质的定义为:对于所有的结点 u\in V-\{s\} ,\sum\limits_{v\in V}f(v, u)\geq 0 。
(p431)我们修改超额流 e 的定义为:\begin{aligned} &e(u)=\sum\limits_{v\in V}f(v, u) &(26.14) \end{aligned} 。
(p432)PUSH(u, v) 的伪代码需要修改,具体见本节后文的“对 PUSH(u, v) 的修改 ”。可以注意到如此修改后,PUSH(u, v) 同样能保证 f 的容量限制,并且也能保证 f(u, v)=-f(v, u) 的性质。
(p433)INITIALIZE-PREFLOW(G, s) 的伪代码需要修改,具体见本节后文的“对 INITIALIZE-PREFLOW(G,s) 的修改 ”。
(p434)我们对由 INITIALIZE-PREFLOW(G,s) 创建的初始预流 f 的定义公式需要修改,修改为:\begin{aligned} &(u, v).f=\left\{\begin{aligned}&c(u, v) &\text{If } u=s \\ &-c(v, u) &\text{If } v=s \\ &0 &\text{Otherwise} \end{aligned}\right. &(26.15)\end{aligned} 。
对 PUSH(u, v) 的修改:
PUSH(u,v){
// Applies when: u is overflowing,c_f(u,v)>O, and u.h=v.h+l.
// Action: Push delta_f(u,v)=min(u.e,c_f(u,v))units of flow from u to v.
delta_f(u,v)=min(u.e,c_f(u,v))
(u,v).f=(u,v).f+delta_f(u,v)
(v,u).f=(v,u).f-delta_f(u,v)
u.e=u.e-delta_f(u,v)
v.e=v.e+delta_f(u,v)
}
对 INITIALIZE-PREFLOW(G,s) 的修改
INITIALIZE-PREFLOW(G,s){
for each vertex v belongs to G.V
v.h=0
v.e=0
for each edge (u,v) belongs to G.E
(u,v).f=0
s.h=|G.V|
for each vertex v belongs to s.Adj
(s,v).f=c(s,v)
(v,s).f=-c(s,v) // here added one line
v.e=c(s,v)
s.e=s.e-c(s,v)
}
正确性证明(证明重写)
大部分原来的证明是兼容我们修改后的定义的。这里只对不兼容的证明进行重写。
对 引理 26.1 的重新证明
引理 26.1 设 G=(V,E) 为一个流网络,源结点为 s ,汇点为 t ,设 f 为 G 中的一个流。设 G_f 为由流 f 所诱导的 G 的残存网络,设 f' 为 G_f 中的一个流。那么 式 (26.4) 所定义的函数 f\uarr f' 是G的一个流,其值为 |f\uarr f'|=|f|+|f'| 。
容量限制部分 :
对于容量限制,注意到,对于下界:
\begin{aligned}
(f\uarr f')(u, v) &= f(u, v)+f'(u, v) & \\
& \geq f(u, v)+\left(-c_f(v, u)\right) & \text{因为 } -f'(u, v)=f'(v, u)\leq c_f(v, u) \\
& = f(u, v)-\left(c(v, u)-f(v, u)\right) & \\
& = \left(f(u, v)+f(v, u)\right)-c(v, u) & \\
& = -c(v, u) & \text{因为 } f(u, v)=-f(v, u) \\
\end{aligned}
此外,对于上界:
\begin{aligned}
(f\uarr f')(u, v) &= f(u, v)+f'(u, v) & \\
& \leq f(u, v)+c_f(u, v) & \text{因为 } f'(u, v)\leq c_f(u, v) \\
& = f(u, v)+\left(c(u, v)-f(u, v)\right) & \\
& = \left(f(u, v)-f(u, v)\right)+c(u, v) & \\
& = c(u, v) & \\
\end{aligned}
流量守恒限制部分 :
对于所有的结点 u\in V-\{s, t\} ,我们有:
\begin{aligned}
\sum\limits_{v\in V}(f\uarr f')(u, v) &= \sum\limits_{v\in V}\left(f(u, v)+f'(u, v)\right) & \\
&= \sum\limits_{v\in V}f(u, v)+\sum\limits_{v\in V}f'(u, v) & \\
&= 0+0 &\text{(因为流量守恒)} \\
&= \sum\limits_{v\in V}f(v, u)+\sum\limits_{v\in V}f'(v, u) & \\
&= \sum\limits_{v\in V}\left(f(v, u)+f'(v, u)\right) & \\
&= \sum\limits_{v\in V}(f\uarr f')(v, u)
\end{aligned}
值计算部分 (直接展开项,甚至比原证明更简单了):
\begin{aligned}
|f\uarr f'| &= \sum\limits_{v\in V}(f\uarr f')(s, v) & \\
&= \sum\limits_{v\in V}\left(f(s, v)+f'(s, v)\right) & \\
&= \sum\limits_{v\in V}f(s, v)+\sum\limits_{v\in V}f'(s, v) & \\
& =|f|+|f'| &(26.7) \\
\end{aligned}
对 引理 26.4 的重新证明
引理 26.4 设 f 为流网络 G 的一个流,该流网络的源结点为 s ,汇点为 t ,设 (S,T) 为流网络 G 的任意切割,则横跨切割 (S,T) 的净流量为 f(S,T)=|f| 。
对于任意结点 u\in V-\{s, t\} ,重写流量守恒性质如下:
\begin{aligned}
&\sum\limits_{v\in V}f(u, v)=0 &(26.11) \\
\end{aligned}
类似原证明,根据式 (26.11) 对 |f| 的定义,并将式 (26.11) 左面的项加进来,针对所有结点 S-\{s\} 进行求和,并将右面的求和项展开并重新组合,我们得到:
\begin{aligned}
|f| & =\sum\limits_{v\in V}f(s, v)+\sum\limits_{u\in S-\{s\}}\left(\sum\limits_{v\in V}f(u, v)\right) & \\
& =\sum\limits_{v\in V}f(s, v)+\sum\limits_{v\in V}\sum\limits_{u\in S-\{s\}}f(u, v) & \\
& =\sum\limits_{v\in V}\left(f(s, v)+\sum\limits_{u\in S-\{s\}}f(u, v)\right) & \\
& =\sum\limits_{v\in V}\sum\limits_{u\in S}f(u, v) & \\
\end{aligned}
因为 V=S\cup T 且 S\cap T=0 ,我们可以将上述表达式中针对集合 V 的求和分解为针对 S 和 T 的求和,得到:
\begin{aligned}
|f| & =\sum\limits_{v\in V}\sum\limits_{u\in S}f(u, v) & \\
& =\sum\limits_{v\in T}\sum\limits_{u\in S}f(u, v)+\sum\limits_{v\in S}\sum\limits_{u\in S}f(u, v) & \\
& =\sum\limits_{u\in S}\sum\limits_{v\in T}f(u, v) & \text{(消去后一项的依据是性质 } f(u,v)=-f(v,u) \text{)} \\
& =f(S, T)
\end{aligned}
对 推论 26.5 的重新证明
推论 26.5 流网络 G 中任意流 f 的值不能超过 G 的任意切割的容量。
设 (S,T) 为流网络 G 的任意切割,设 f 为 G 中的任意流。根据 引理 26.4 和容量限制性质,我们有:
\begin{aligned}
|f| & =f(S, T) & \\
& =\sum\limits_{v\in S}\sum\limits_{u\in T}f(u, v) & \\
& \leq \sum\limits_{v\in S}\sum\limits_{u\in T}c(u, v) & \\
& =c(S, T)
\end{aligned}
对 定理 26.6 的重新证明
定理 26.6(最大流最小切割定理) 设 f 为流网络 G=(V,E) 中的一个流,该流网络的源结点为 s ,汇点为 t ,则下面的条件是等价的:
残存网络 G_f 不包括任何增广路径。
原证明是兼容的。
$(2)\Rarr(3)$:
原证明几乎完全兼容,只需要做些微修改(**修改部分已加粗**):
假定 $G_f$ 不包含任何增广路径,也就是说,在残存网络 $G_f$ 中不存在任何从源结点 $s$ 到汇点 $t$ 的路径。定义 $S=\{v\in V:\text{在 } G_f \text{ 中存在一条从 } s \text{ 到 } v \text{ 的路径}\}, T=V-S$。显然,$s\in S$,而因为 $G_f$ 中不存在从 $s$ 到 $t$ 的路径,所以 $t\not\in S$。因此,划分 $(S,T)$ 是流网络 $G$ 的一个切割。现在考虑一对结点 $u\in S$ 和 $v\in T$。如果 $(u,v)\in E$,则必有 $f(u,v) =c(u, v)$,因为否则边 $(u,v)$ 将属于 $E_f$,而这将把结点 $v$ 置于集合 $S$ 中。如果边 $(u,v)\not\in E$ 且边 $(v,u)\in E$,则必有 $f(v,u)=0$,因为**否则 $c_f(u, v)= c(u, v)-f(u, v)=0+f(v, u)$ 将为正值**,于是边 $(u,v)$ 将属于 $E_f$,而这将把结点 $v$ 置于集合 $S$ 中。当然,如果边 $(u,v)$ 和边 $(v,u)$ 都不在集合 $E$ 中,则 $f(u,v)=f(v, u)=0$。**因此有**:
$$
\begin{aligned}
f(S, T) &=\sum\limits_{v\in S}\sum\limits_{u\in T}f(u, v) & \\
& = \sum\limits_{v\in S}\sum\limits_{u\in T}c(u, v) & \\
& =c(S, T)
\end{aligned}
$$
根据 引理 26.4,$|f|=f(S, T)=c(S, T)$。
$(3)\Rarr(1)$:
原证明是兼容的。
### 对 引理 26.9 的重新证明
> **引理 26.9** 设 $G=(V,E)$ 为一个二分图,其结点划分为 $V=L\cup R$,设 $G'=(V',E')$ 是图 $G$ 所对应的流网络。如果 $M$ 是 $G$ 中的一个匹配,则流网络 $G'$ 中存在一个整数值的流 $f$,使得 $|f|=|M|$。相反,如果 $f$ 是 $G'$ 中的一个整数值的流,则 $G$ 中存在一个匹配 $M$,使得 $|M|=|f|$。
这个引理大部分的论述也是兼容的,只有前半部分(前两段)需要做些微修改,以及后半部分有一句话可能需要一些补充说明。
**对于前半部分**(原证明的前两段,对正向论断的证明),只需要做些微修改(**修改部分已加粗**):
首先证明图 $G$ 中的一个匹配 $M$ 对应流网络 $G'$ 中一个整数值的流 $f$。定义流 $f$ 如下:如果 $E'$ 中的边 $(u,v)\in M$,则 $f(s,u)=f(u, v)=f(v, t)=1$,**以及 $f(u, s)=f(v, u)=f(t, v)=-1$**。对于所有其他属于 $E'$ 的边 $(u,v)$,定义 $f(u,v)=0$。可以很容易地验证这样所定义的流 $f$ 满足容量限制和流量守恒性质。
从直观上看,每条边 $(u,v)\in M$ 对应流网络 $G'$ 中流经路径 $s\rarr u\rarr v\rarr t$ 的一个单位的流。而且,除了源结点 $s$ 和汇点 $t$ 之外,由子集 $M$ 中的边所诱导的路径都是结点不相交的(指除 $s$ 和 $t$ 之外,两条不同的路径中不存在相同的结点)。横跨切割 $(L\cup \{s\}, R\cup \{t\})$ 的净流量等于 $|M|$。因此,根据 引理 26.4,流 $f$ 的值为 $|M|$。
**对于后半部分**:
在原书的 引理 26.9 的证明中,在 p430 页第一自然段(公式不计入自然段)的第二行,有一句:
> “...,而如果有 $1$ 个单位的流进入,根据流量守恒性质,离开该结点的流也必须有 $1$ 个单位。”

可以注意到,这个论述基于我们修改后的流量守恒性质也是能推出的。
基于我们修改后的流量守恒性质,例如,流入结点 $u$ 的流大小可以被定义为 $I(u)=\sum\limits_{v: V|f(u, v)>0}\left(f(u, v)\right)$,流出结点 $u$ 的流大小可以被定义为 $O(u)=\sum\limits_{v: V|f(u, v)<0}\left(-f(u, v)\right)$。
设 $U_1=\{v: V|f(u, v)>0\}, U_2=\{v: V|f(u, v)<0\}$,显然有 $U_1\cap U_2=\empty$;并且对于 $v'\in V-U_1-U_2$,还显然有 $f(u, v')=0$。
所以为了满足流量守恒性质 $\sum\limits_{v\in V}f(u, v)=0$,每当流入结点 $u$ 的流大小 $I(u)$ 增加 $1$,必然对应地有流出结点 $u$ 的流大小 $O(u)$ 增加 $1$。
### 对原书 `RELABEL` 伪代码后部分论述的重新证明
这段论述的位置为 p433 页 `RELABEL` 伪代码之后,“**通用算法**”小节之前。

修改后的版本为(**修改部分已加粗**):
当调用操作 `RELABEL(u)` 时,我们称结点 $u$ 被重贴标签。注意,当结点 $u$ 被重贴标签时,$E_f$ 必须包含至少一条从结点 $u$ 发出的边,这样将使得代码中的求最小值操作所针对的对象不是一个空集。这条性质可以从结点 $u$ 是一个溢出结点的假设推导出来,**而这又会进一步告诉我们**:
$$
\begin{aligned}
u.e =\sum\limits_{v\in V}f(v,u) > 0
\end{aligned}
$$
**由此可得,必然至少有一个结点 $v$,使得 $(v,u).f>0$**。但是,$c_f(u,v)>0$ 则意味着 $(u,v) \in E_f$。因此,操作 `RELABEL(u)` 给结点 $u$ 所赋予的高度是高度函数所能允许的最大高度。
### 对 引理 26.19 的重新证明
> **引理 26.19** 设 $G=(V,E)$ 是源结点为 $s$ 汇点为 $t$ 的一个流网络,设 $f$ 是 $G$ 中的一个预流。那么对于任意溢出结点 $x$,在残存网络 $G_f$ 中存在一条从结点 $x$ 到源结点 $s$ 的简单路径。
原证明的基本思路其实是兼容的,只需要做部分修改(**修改部分已加粗**):
对于溢出结点 $x$,设 $U=\{v: \text{在 } G_f \text{ 中存在一条从结点 } x \text{ 到结点 } v \text{ 的简单路径}\}$,并且为了使用反证法,假设 $s\not\in U$,并设 $\overline U=V-U$。
使用式 $(26.14)$ 对超额流量的定义,对 $U$ 中的所有结点求和,并注意到 $V=U\cup \overline U$,**我们获得**:
$$
\begin{aligned}
\sum\limits_{u\in U}e(u) &=\sum\limits_{u\in U}\sum\limits_{v\in V}f(v, u) & \\
& =\sum\limits_{u\in U}\left(\sum\limits_{v\in \overline U}f(v, u)+\sum\limits_{v\in U}f(v, u)\right) & \\
& =\sum\limits_{u\in U}\sum\limits_{v\in \overline U}f(v, u)+\sum\limits_{u\in U}\sum\limits_{v\in U}f(v, u) & \\
& =\sum\limits_{u\in U}\sum\limits_{v\in \overline U}f(v, u) & \text{(消去的依据是 } f(u,v)=-f(v,u) \text{)}\\
\end{aligned}
$$
我们知道,$\sum\limits_{u\in U}e(u)$ 必然为正值,因为对于 $x\in U, e(x)>0$,除源结点 $s$ 以外的所有结点都有非负值的超额流量,并且根据假设,$s\not\in U$,**因此有**:
$$
\begin{aligned}
& \sum\limits_{u\in U}\sum\limits_{v\in \overline U}f(v, u)>0 & (26.17) \\
\end{aligned}
$$
**可以发现**,如果式 $(26.17)$ 成立,则至少存在一对结点 $u'\in U, v'\in E$ 且有 $f(v', u')>0$。但是,如果 $f(v', u')>0$,则必有一条残存边 $(u', v')$,这就意味着从结点 $x$ 到结点 $v'$ 存在一条简单路径(路径 $x\rightsquigarrow u'\rarr v')$,而这与 $U$ 的定义矛盾。
## 实现细节
其实也没有太多的实现细节。
算法除了对 `PUSH(u, v)` 和 `INITIALIZE-PREFLOW(G,s)` 的改动,其它部分完全按照原书描述实现即可;`PUSH(u, v)` 和 `INITIALIZE-PREFLOW(G,s)` 改动的内容实质上也并不多。
如果实在需要参考的话,可以看下 [这份实现](https://www.luogu.com.cn/paste/fpiwlmd0)。
## 结语鲜花
因为改了很多前面的(关于流网络的)基本定义,刚开始校对和重写证明时发现需要改的地方比预期要多得多(26.1~26.5),甚至一度以为文章会变得非常非常长。
但因为后面的证明许多都是直接引用前面的引理和推论,而非直接涉及流网络定义了,并且虽然我们改了定义但是却没有对引理和推论本身产生任何影响,所以后面需要重写和修正的地方也就越来越少。最后得到的篇幅其实也并不算长,确实符合一开始所谓“打补丁”的预期()。