双搜其实并不难

关于双向BFS的一点学习


双搜有一个不短的英文名Bi-Directional Breadth-First-Search(双向广度优先搜索)


                              


        先来说一下什么是搜索吧。根据我的理解,搜索就是根据某种扩展规则,从某一个(连通图)或几个(非连通图)点(或状态)开始,遍历所有可能达到的点(或状态),简单来说就是遍历所有状态,找出其中的可行解(或最优解)。有很多问题像一个事物可以变化出很多状态,所求涉及到起止状态间的线路、步数、可达性等,用搜索来做就比较合适了。


        搜索的初学者通常可能会觉得像BFS、DFS这些东西已经够难的了,但有时使出浑身解数写出很长的搜索代码还是会T.L.E、M.L.E,这充分说明这两种最最基础的盲目搜索方法是比较低效的。双向BFS就是对BFS的一种比较高级的优化,仅能用在起始状态和目标状态均已知的情况下,基本思想就是把起始点和目标点都看作是起点,两个起点同时出发,只要有路就会碰头。下面我就说一下怎么把BFS优化成双搜(可能还有三搜甚至多搜,原理差不多,就不一一讨论了,以下只讨论双搜)。


        当然还是要用队列,不过原先的一个已经不够了,双搜要用两个,开始时要把两个搜索起点first1和first2分别放入队列,然后向普通BFS一样开始搜索就行了;循环结束条件可以不变,因为如果其中一个队列跑完了还没有出结果,那就是没有路了;visit数组不用变,但用法有点不同了,不能再用bool型,可以用short、char之类的,为什么这样呢?因为仅“走过”和“没走过”已经不能满足双搜的visit需求了,双搜的visit有三种状态,分别是:①队列1走过、②队列2走过、③没走过;循环里的内容要扩增一倍,进入循环后先把一个起点扩增一层,再去扩另一个起点,此重循环结束;按照先前的想法,碰头时结束,返回此点到两起点的距离和,判断碰头的方法就是队1走到队2的地牌上或反过来;还有最后一点要注意,如果据题意建出的图是无向图,那么两边扩展方法就应该是一样的,但如果是有向图,起点要正着扩展,终点则要反着。差不多就这些了。


        听过来人说,搜索部分的最重难点在剪枝(无穷无尽神鬼难测想破脑袋也想不到的剪枝啊,给跪……)比如奇偶剪枝、迭代加深、xxxx、……,双搜也有一个比较固定的剪枝方法,我管它叫“平衡队列”,提供速度的关键在于使状态扩展得少一些,所以优先选择队列长度较少的去扩展,保持两边队列长度平衡。这比较适合于两边的扩展情况不同时,一边扩展得快,一边扩展得慢。


        下面来说一说为什么双搜比单搜快。我们不妨假设每次搜索的分支因子是r,如果最短的路径长为L的话(也就是搜了L层),那么,用一般的BFS算法(不考虑去掉重复状态),总的搜索状态数是r^L(^表示乘方运算);而如果采取双向BFS算法,那么,从前往后搜,我们只需要搜索L/2层,从后往前搜,我们也只要搜L/2层,因此,搜索状态数是2*(r^(L/2)),比普通BFS就快了很多了。 


参考资料:

【1】http://www.cppblog.com/Yuan/archive/2011/02/23/140553.aspx

【2】http://blog.sina.com.cn/s/blog_6635898a0100p4wd.html


PS:搜资料时找到一篇很好的搜索算法图示比较,很详细,不过因为太过高端,没能引用的上。

http://www.2cto.com/kf/201104/87378.html






你可能感兴趣的:(搜索,语言/理论,搜索,双向BFS)