最大流算法之压入与重标记算法(预流推进算法)
1.算法发现者:
Goldberg && Tarjan( 87 ):
不了解Goldberg,不过对Tarjan有所了解,他还是斐波那契堆,LCA,强连通分量Tarjan算法的发现者,也是算法大师高德纳的弟子。
2.算法动机:
在一个有向图中,寻找从源点( source )到汇点( sink )的最大流( max flow );
3.算法时间复杂度:
O(V*V*E);
4.最大流算法的一些名词定义,限制:
A. 流网络( flow network ) :
G = ( V, E ) 的有向图;
B. vertex s : 源点 && vertex t : 汇点;
C. 每条edge( u, v )∈E 有 capacity( u, v ) > 0;
若 ( u, v ) ∉ E 有 capacity( u, v ) = 0;
D. 流量图(flow map):
edge(u,v)的当前已行流的大小 f(u, v) ≤ capacity(u, v) && f(u, v) = - f(u, v)(反对称);
E. 饱和边( saturated edge(u, v) ):
capacity(u, v) == f(u, v);
F. edge(u, v)的残余量 residual(u, v):
capacity(u, v) - flow(u, v);
G. 残余图( residual graph ) RG( V, E' ):
edge( u, v ) ∈ E' 有 residual(u, v) >= 0;
H. 增广路( augmenting path ):
在残余网络RG上一条从 s 到 t 的可达路径;
I. 最大流(max flow):
无增广路存在;
5.压入与重标记算法引入的新名词的定义:
A. 余流(excess flow):
依旧储存在该顶点,尚未排出的流量 excess(u) = ∑ f(V,v) >= 0;
B. 活性点(active vertex)v:
v ∈ V - { s, t } && d( v ) < ∞ && excess( v ) > 0,该点流量溢出;
C. 点的高度height(u):
每个顶点被赋予一个高度,余流只能从高往下压入比他低的顶点;
D. 合法标记(legal labeling):
edge(u, v) ∈ E,height(u) <= height(v) + 1,算法仅仅将流从u 压入 v 当 height(u) = height(v) + 1;
6.算法抽象思想:
和Edmonds-Karp, Dinic思想算法不同,在残余网络RG中,算法不寻找增广路AP。
算法假设每个顶点u存在无限大容量的水库,可以用来储存任意多的水,
储存在该顶点的流量被称为该点的余流(excess(u)) 。
算法在开始时,使得所有从源点 s 出来的边,达到饱和状态,即saturated edge。
然后努力使得这个前置流( pre flow )达到汇点 t。然而,无法达到t的流量,全部返回给源点 s 。
算法开始时,除了height(s)= |V|,height(t)= 0(固定不变),
其余点都初始化为height(u)= 0(可变);
每个顶点与它相关联的的顶点处于同一平台,然而需要Push(u)的流量的时候,
需要适当调整height(u)(即重标记relabel(u)),才可以使得excess(u),流向高度小于它的顶点v。
但是高度较低的顶点到高度较高的顶点可能存在一条正向网络流,但是“对流”的处理,都是往下的。
需要注意的是,调整height(u)时,需要增加到比其最低的相邻顶点 v 的高度高一个单位,
也就是height(u) = height(v) + 1;
下面解释算法的具体操作。
7.算法流量限制:
A. ∀(u,v)∈ V => f(u, v)<= capacity(u, v)(容量限制)
B. ∀(u,v)∈ V => f(u, v) = - f(u, v)(反对称性)
C. ∀ v ∈ V - { s } => ∑ f(u,v) >= 0
( 不满足基尔霍夫电压定律,即流守恒的特性 ,但是满足放宽条件下的流守恒特性 )
8.算法注意点:
A. 余流的处理(excess handling):
算法尽量将余流压入汇点 t,若是还有一部分余流无法达到 t,则将所有剩下的余流返回给源点 s。当∀ u ∈ V 有 excess(u) == 0,那么算法终止,且得到了最大流。
B. 有效的高度标签(height label):已提过
9.算法基本操作:
A. 压入( push(u, v) ):
if { residual(u,v) > 0 && height(u)== height(v) + 1 }(仅对活性点操作):
delta = min(excess(u),residual(u, v) );
f(u,v) += delta; f(v,u) -= delta;
excess(u) -= delta; excess(v) += delta;
B. 重标记( relabel(u) ):
if{ ∀ v ∈ V && residual(u,v) > 0 }
then{ 选择出高度最小的点 v; }
height(u) = height(v) + 1;
压入,重标记中任一操作仅对活性点有效:
if( residual(u,v) > 0 && height(u) == height(v) + 1 ):
push(u);
otherwise :
relabel for all residual(u,v)in RG;
10.算法优化:
+动态树 O(VE log(V2/E));
11.实现代码:
#include <iostream>
#include <cstring>
using namespace std;
const int MAX_SIZE = 100;
const int INF = 1 << 30;
int capacityGraph[MAX_SIZE][MAX_SIZE];
int flowMap[MAX_SIZE][MAX_SIZE];
int height[MAX_SIZE];
int excess[MAX_SIZE];
int src, des;
int vertex_num, edge_num;
void init()
{
memset( capacityGraph, 0, sizeof( capacityGraph ) );
memset( flowMap, 0, sizeof( flowMap ) );
memset( height, 0, sizeof( height ) );
memset( excess, 0, sizeof( excess ) );
cout << "Enter vertex num && edge num : ";
cin >> vertex_num >> edge_num;
for( int i = 1; i <= edge_num; ++i )
{
int start, end, cap;
cout << "Enter start && end && capacity : ";
cin >> start >> end >> cap;
capacityGraph[start][end] = cap;
}
src = 1;
des = vertex_num;
height[src] = vertex_num;
}
void preFlow()
{
for( int i = src; i <= des; ++i )
{
if( capacityGraph[src][i] > 0 )
{
const int flow = capacityGraph[src][i];
flowMap[src][i] += flow;
flowMap[i][src] = - flowMap[src][i];
excess[src] -= flow;
excess[i] += flow;
}
}
}
void push( int start, int end )
{
int flow = min( excess[start], capacityGraph[start][end] - flowMap[start][end] );
flowMap[start][end] += flow;
flowMap[end][start] = -flowMap[start][end];
excess[start] -= flow;
excess[end] += flow;
}
bool reLabel( int index )
{
int minestHeight = INF;
for( int i = src; i <= des; ++i )
{
if( capacityGraph[index][i] - flowMap[index][i] > 0 )
minestHeight = min( minestHeight, height[i] );
}
if( minestHeight == INF )
return false;
height[index] = minestHeight + 1;
for( int i = src; i <= des; ++i )
{
if( excess[index] == 0 )
break;
if( height[i] == minestHeight && capacityGraph[index][i] > flowMap[index][i] )
push( index, i );
}
return true;
}
void pushReLabel()
{
bool flag = true;
preFlow();
while( true )
{
if( flag == false )
break;
flag = false;
for( int i = src; i <= des - 1; ++i )
{
if( excess[i] > 0 ) flag = flag || reLabel( i );
}
}
}
int main()
{
init();
pushReLabel();
cout << "max flow : " << excess[des] << endl;
return 0;
}