要解决的问题:
给定一个迷宫,此迷宫中有且仅有一个入口和出口,其中设有若干检查点,要求从入口开始,经过所有检查点后到达出口所需的最短路径。其中路径中允许多次经过入口或出口或某检查点,但路径的开始和结尾必须分别是入口和出口。更形象一点就是要把图中所有的宝藏找出来带出去的问题。
连设计算法+写算法实现的论文+编写代码和制作演示动画,花费了四天时间,还是小有收获的赶脚。算法的核心描述就是先假设已经有一个最优路径,然后插入一个新的检查点时,如何使插入导致的路径增量最小。
这种先假设存在最优路径然后再在这条假设的路径中插入新的结点的思想其实就是贪心算法,出现在很多算法当中,并且十分行之有效,其基本模型就是持有两个关键集合,根据条件改变集合元素来动态规划集合中元素的组织方式。此题中的寻宝算法十分类似于货郎担问题,区别在于,货郎在寻找旅行路线时不能经过已经走过的城市,且最终要回到出发点,而这里的模型是可以经过已经走过的城市,但最终不是要回到出发点,而是以达到一个指定点结束。但本质上都需要两个动态集合来保存这些代表地点的结点。货郎担问题相对于此问题的简单之处表现在其不重复的特点上,因为不重复,所以每个结点必有单前驱与单后继,集合中元素都不相同,组合起来就很简单。而此问题由于可以多次经过同一个节点,所以在一条路径上就存在多个相同的结点,这就导致了同一结点有多前驱或多后继的问题。本问题解决的关键就是要解决这种多前驱与多后继的问题,也就是要决定同一结点如何在集合中表现出多种身份的方式。我在设计此算法时,到此便知道这就是问题的核心了,最终想到的办法就是为多前驱或多后继的结点建立多个虚拟结点,这些虚拟结点实质上共享同一个位置,但却分别有着不同的前驱与后继,因此可以构成一条完成的路线。然后问题就转移到如何在两个集合中标识这些虚拟结点的身份了。剩下的问题不再是难题,除了需要计算在插入新结点时如何查找最优插入区间、如何计算插入后带来的代价增量、如何决定是采用正常插入模式还是采用创建新的虚拟结点的插入方式等问题了。其中用到了算法导论一书中介绍的“三角不等式(两边之和大于第三边,两边之差小于第三边)”原理来计算和比较代价增量。
最后,如设计算法时所描述,这其实是一个NP完全问题,无法验证它是最优的,插入时存在稳定性改变的问题,但绝大多数情况下,得到的结果就是我们想要的最优结果。同时,整个寻路过程中,BFS算法的时间复杂度为O(logn),其中包括前向找出口的logn的时间和回退的logn的时间以及路径方向整理的logn的时间,这个总的时间复杂度虽然不算特别大,但却经常需要计算两点间的最短路径,而在很多情况下,需要重复计算上一次所得的结果。因此在算法中适时保存和处理中间结果将能够大大提高算法的时间性能。
最终的运行结果如图所示: