SPFA_dfs的优化

 

摘自WC2009 姜碧野的《SPFA 算法的优化与应用》

poj对就的题目有3621 2949

1.2.1:基于 Dfs 的 SPFA的基本原理
在上面的介绍的算法中,我们用一个循环队列来存储需要继续迭代的节点,
实现时使用类似广度优先搜索的方式。那么我们是否可以使用搜索的另一利器:
深度优先呢?
回忆之前的算法,由于采用广度优先的思想,每当我们扩展出一个新的节点,总是把它放到队列的末尾,其缺点是中断了迭代的连续性。而实际上如果采用深
度优先的思想,我们可以直接从这个新节点继续往下扩展。于是算法的实现方式
可以改成不断从新节点往下递归进行求解。
而对于负环的判断则显得更为简单,因为假如存在负环a1->a2->….ak->a1,
那么算法运行时,会从某个点a1开始Dfs,最后又回到了这个点。所以只需用一个
辅助数组记录当前节点是否在递归栈中便可及时检测出负环。
伪代码如下:
《SPFA_dfs的优化》

两种算法的原理相同,相对 Bellman-Ford的优化也是殊途同归的。其精髓都
在于只保存有用的状态。
那么,SPFA的这种实现效率如何呢?在随后的初步尝试中,笔者发现,在一
些拓扑关系比较强的时候,广度会有很大的优势。Dfs 往往由于不断盲目地迭代
而耗费了太多时间甚至远远超过时间的限制。
因此在下文中,笔者将结合深度优先搜索的特点,尝试对其进行优化。

1.2.2:基于 Dfs 的 SPFA的相关优化
Dfs 的实现方式虽然暂时无法取得好的效果,但我们并不应该就此放弃,Dfs
相对 Bfs 灵活的架构必能给予我们广阔的优化空间。
首先,一个很自然的想法便是改变搜索顺序,即把边按照一定的规则排序,
在这里当然是把边权按优劣排序,这样便可以得到一个不错的初始解。加入了这
个优化后,在某些数据上取得了不错的效果,但许多数据依然远远超过时间限制。             
接着笔者联想到网络流使用贪心初始流进行优化的方法,因为当前解的优劣
程度对之后的迭代有很大影响。而且 Dfs 的低效很大程度上是因为频繁地使用较
次解去更新大片的元素。
由此我们可以考虑先在第一次 Dfs 时,对扩展节点采取一些限制以快速求得
一个较优解,然后再进行第二次完整的 Dfs。笔者经过多次尝试,在针对随机数
据上得出一种不错的贪心初始解方法。

《SPFA_dfs的优化》

即对于每个节点只更新一个边后便退出。这一算法对于随机图能取得不错的
效果,因为往往只更新一次能快速将当前值快速地传递。加入这个优化后效率有
了很大提高,但与Bfs 还是有不少差距。

于是笔者转而分析 Bfs 与 Dfs 的不同,经过比较可以发现 Bfs 的优势主要体
现在某个点出队前可能再次被更新而得到更优的解进行下一次迭代。而 Dfs 往往
会用一个次解进行层次很深的迭代(一个次解会导致 O(N)级别的更新)。由此,可
以联想到深度优先搜索的一个改进版本:迭代加深搜索,结合前面贪心初始解的
原理,我们可以通过限制节点递归的深度并逐步放宽限制求解。
加入这个优化后,笔者欣喜地发现,在 Bfs 不擅长的网格形数据中,Dfs 所
用时间仅为 Bfs 的 1/3,效率飞速提高,优势明显。而在一些其他随机数据中效果
则有好有坏,总体还是比 Bfs 略逊一筹,但差距已不大。值得注意的是:对深度
限制的不同,时间效率也有差异,笔者总结出了两种效果相对不错的限制方法:
1 依次设置深度上限为1,2,4,8,16,32,64…..2^k….. maxlongint
2 依次设置深度上限为20,30,40,50,60,80,100….maxlongint
鉴于Dfs 在网格图上的高效,笔者即将其应用在 Spoj 的 skivall 一题中。该题
的一个可行算法即为网格图上的费用流。使用 Dfs 后,效率得到了很大提升,仅
用 3s 多就通过了全部数据。而且在 USACO Jan09 Travel 一题的数据中,DFS
相比 BFS 有大幅提高,甚至优于 dijikstra。(当然与数据的特性有关)
    至此,对 Dfs 的优化也就告一段落了。但可以预见,Dfs 可行的优化远远不
止如此,还有许多性质没有得到很好的利用。而且在上文的优化中,笔者并没有
牺牲 SPFA 的简洁性(几乎不需要怎么修改原程序),因此许多诸如加入优先队列
(堆)等方法还有待研究。也希望上文的方法能激发读者的思路,继续探索新的优
化方法,或是将这类优化思想应用在其他相关领域。

    原文作者:DFS
    原文地址: https://blog.csdn.net/zz_ylolita/article/details/14052985
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞