再谈预流推进 - 更快的前置重贴标签算法
· 7 min read
何为再谈?
几天前写了一篇博客:浅谈预流推进网络流算法
其中简单描写了推送-重贴标签算法, 但算导
中还有一个后续章节写到了一个优化的算法, 即前置-重贴标签
另外, 之前那篇博客的内容有一些错误, 懒得再改了
如果我的变量名很难看的话, 请原谅
分析-准备
前置-重贴标签
是对推送-重贴标签
的优化, 那么他们一定是类似的, 或者说, 代码会变长一丢丢
设想, 推送-重贴标签
慢到了哪里? (类比 spfa 的函数) 就是那个 while() 语句! 所以说我们需要针对 while() 语句进行优化
在推送流量时, 推送
算法是遍历从当前节点发出的所有边并尝试推送流量到高度较低的邻节点, 但是细想可以发现: 还没有扫完所有边就已经推送完流量时, 我们可以保存下当前的边
为什么? 我们把要讨论的节点称为当前节点, 假设上次调用出现了上述情况, 我们把上次处理当前节点之后储存的边叫做当前边, 证明:
- 前提条件: 根据预流推进的
特性
, 所有节点的高度值是不会下降的, 不再证明 - 再次处理当前节点, 一定是因为其他节点推送来了流量
- 假如是从当前边前面的边推送来的, 那条边对应的节点一定比当前节点高, 而且由于上次推送, 当前边之前的边已经不存在可推送流量(由于高度限制或边容量为零)
- 由上一条可知, 再次从头扫描到当前边花费的时间是无效的 不必要的, 所以我们记录当前边是有意义的
知道了这一点, 就可以优化代码了, 再加上上一篇中提到的队列优化, 这种实现方式曾经由 Tarjan 与另一个人共同提出, 根据算导-本章注记
所说, 这种实现的期望复杂度已经优化为V^3
了, 跟我们要讨论的前置-重贴标签
期望复杂度一样! 直接用呗:
//代码写跪, 留个坑
我也不明白为什么的链表优化
可是心里虚, 还是默默尝试一下前置
吧, 算导
说了, 前置
用了链表优化, 但是后面的证明太长, 本着汝佳的不求甚解精神, 我也不管为什么了, 虽然我不知道为什么这样会快, 但是我知道这样一定不会错, 为什么?
- 我相信
算导
, 没了
链表优化是什么呢?
我们吧所有节点组织成链表, 起始时按拓扑序排列, 然后从表头开始处理:
- 对当前节点进行推送操作
- 如果已经扫过当前节点的所有边, 重置当前边(也就是循环扫描)
- 如果还有未推送流量, 提升当前节点高度, 并将当前节点移至链表头
- 如果没有执行 3 ,处理链表中的下一个节点
- 到达链表尾, 退出