问题描述
具体的最大流问题定义、术语以及特性,前人已经描述的很详细了:
我们需要注意以及几个方面:
- 流量守恒约束:这个无需多言。
- 后向边(反向边):尾列在头的前面:1→…→i←j→…→n。引入后向边的目的在于寻找到更多可能的增益路径,而无需回溯后寻找,减少时间复杂度。
- 生成增益路径的方法决定了问题的时间复杂度。——>
增益路径的生成次序如果不恰当,会对方法的效率有巨大的影响。如下:其中U代表某个大正整数。如果沿着路径1→2→3→4对流量0进行增益,得到(b)中值为1的流量;接着沿着路径1→3←2→4对流量0进行增益,得到(c)中值为2的流量。按照上述继续增益,需要2U次迭代才能得到最大的流量值2U。但是,如果沿着路径1→2→4和路径1→3→4增益U仅需要2次迭代。2U和2的巨大差异说明选择高效方法的重要性。
最短增益路径算法
也加先标记先扫描算法。其思想就是按照广度优先查找,用数量最少的边来生成增益路径。
时间复杂度:证明了增益路径的数量不会超过nm / 2,其中,n和m分别是顶点和边的数量。用邻接矩阵,广度优先算法求一条增益路径的时间属于O(n+m) = O(m),最短增益路径算法的时间效率属于O(nm 2 2 )。
伪代码——>
ShortestAugmentingPath(G)
//最短增益路径算法的实现
输入:网络G,具有一个源点1和一个汇点你,每条边(i,j)的容量都是正整数Uij
输出:最大流量X
对网络中的每条边(i,j),设Xij = 0
把源点标记为∞/-,再把源点加入到空队列Q中
while not Empty(Q) do
i ← Front(Q); Dequeue(Q)
for 从i到j的每条边 do //前向边
if j没有被标记
Rij ← Uij - Xij
if Rij > 0
lj ← min{li,Rij}; 用lj/i+来标记j
Enqueue(Q,j)
for 从j到i的每条边 do //后向边
if j没有被标记
if Xji > 0
lj ← min{li,Xji}; 用lj/i-来标记j
Enqueue(Q,j)
if 汇点被标记了
//沿着找到的增益路径进行增益
j ← n //从汇点开始,用第二个标记反向移动
while j ≠ 1 //没有到达源点
if 顶点j的第二个标记是i+
Xij ← xij + ln
else //顶点j的第二个标记是i—
Xji ← Xji -ln
除了源点,擦去所有顶点的标记
用源点对Q重新初始化
return X //当前的流量是最大的
例子——>
补充
- 网络中的最大流量值等于它最小割的容量(如:上例中的最小割是{(1,2),(4,3)},容量是3)
- 所有已标记顶点到未标记顶点的边就构成了最小割
- 如果从未标记顶点到已标记顶点的边,他们一定是空的(边上没有流量)
- 预流算法的最坏时间复杂度可以达到O(nm)
参考:
- 算法设计与分析基础
- 最大流算法之EK(最短路径增广算法)
- 最大流(网络流基础概念+三个算法)