PushRelabel 压入与重标记算法

问题

用压入与重标记算法求网络G=<V,E>G = <V,E>的最大流,GG是单源点、单汇点,边的容量都为正整数的网络。

定义

设网络中每个节点都有一个水位高度levellevel,当cf(i,j)=c(i,j)f(i,j)>0c_f(i,j) = c(i,j) - f(i,j) \gt 0时边ei,je_{i,j}仍然可以容纳更多的流,当cf(i,j)=0c_f(i,j) = 0时称边ei,je_{i,j}为饱和边,不能容纳更多的流。

设节点viv_iviV\{s,t}v_i \in V \backslash \{s, t\})的流入和流出之差为:

x(i)=inflowioutflowi=uVf(u,i)vVf(i,v)x(i) = inflow_{i} - outflow_{i} = \sum_{u \in V} f(u, i) - \sum_{v \in V} f(i, v)

若相邻节点vi,vjv_i, v_j满足cf(i,j)>0c_f(i,j) \gt 0,称vi,vjv_i, v_j之间可以容纳额外的流。

压入操作

压入操作条件:

(1)(1) 相邻节点vi,vjv_i, v_j的水位满足level(i)=level(j)+1level(i) = level(j) + 1(称vjv_jviv_i的低位,viv_ivjv_j的高位);

(2)(2) 相邻节点vi,vjv_i, v_j的边的剩余容量满足cf(i,j)>0c_f(i,j) \gt 0

压入操作:像高处的水流向最低洼的位置,对于满足压入操作条件的相邻节点,由节点viv_i流向节点vjv_j,边ei,je_{i,j}的剩余容量更新为:

{f(i,j)=f(i,j)+Δf(j,i)=f(j,i)Δx(i)=x(i)+Δx(j)=x(j)Δ\begin{cases} f(i,j) = f(i,j) + \Delta \\ f(j,i) = f(j,i) - \Delta \\ x(i) = x(i) + \Delta \\ x(j) = x(j) - \Delta \end{cases}

其中Δ=min(x(i),cf(i,j))\Delta = min(x(i), c_f(i,j))。任意节点viv_i能够流出的最大值为x(i)x(i)(不能凭空制造流,每个节点必须有流入才能流出),而边ei,je_{i,j}能够额外容纳的流为cf(i,j)c_f(i,j),因此实际可用的流是两者的最小值。

网络中将源点视作入流无穷大的节点,即有

inflows=+x(s)=+\begin{matrix} inflow_{s} = + \infty \\ x(s) = + \infty \end{matrix}

将汇点视作出流无穷大的节点,即有

outflowt=x(t)=outflow_{t} = - \infty \\ x(t) = - \infty

重标记操作

重标记操作是调整相邻节点之间的水位高度差的辅助操作,目的是尽可能将更多的流压入汇点。

重标记操作条件:

(1)(1) 节点viv_i的流入和流出之差满足x(i)>0x(i) \gt 0,说明该节点仍然能够制造出流;

(2)(2) 节点viv_i存在可以容纳额外的流的邻节点vjv_j(即cf(i,j)>0c_f(i,j) \gt 0),且水位高度之差满足level(i)level(j)level(i) \leq level(j)

重标记操作:

level(i)=min{level(j)}+1level(i) = min \{ level(j) \} + 1

其中vjv_j是所有满足重标记条件的viv_i的邻节点,将viv_i的水位设置为所有节点中最低的水位加11

解法

初始时设网络中任意两点间的流为00,即f(i,j)=f(j,i)=0f(i,j) = f(j,i) = 0(其中vi,vjv_i ,v_j为相邻节点),可知任意节点viv_i的流入流出差为:

x(i)={+vi=svi=t0viV\{s,t}x(i) = \begin{cases} + \infty & v_i = s \\ - \infty & v_i = t \\ 0 & v_i \in V \backslash \{s, t\} \end{cases}

对源点ss进行预压入操作(无视水位):

x(i)=f(s,i)=c(s,i)x(i) = f(s, i) = c(s, i)

其中viv_i是所有与源点ss相邻,且满足剩余容量cf(s,i)>0c_f(s,i) \gt 0的邻节点。

然后设置网络中节点的水位:

level(i)={Vvi=s0viV\{s}level(i) = \begin{cases} \lvert V \rvert & v_i = s \\ 0 & v_i \in V \backslash \{ s \} \end{cases}

遍历网络找到满足压入操作、重标记操作的相邻节点和边,并进行对应操作。重复这两种操作直到无法继续,算法结束。网络的最大流即为汇点tt的邻节点的出流之和:

flowmax=uVf(u,t)flow_{max} = \sum_{u \in V} f(u, t)

该算法的时间复杂度为O(V2E)O(\lvert V \rvert ^2 \cdot \lvert E \rvert)

Introduction To Algorithms

源码

PushRelabel.h

PushRelabel.cpp

测试

PushRelabelTest.cpp

Last updated