最短路中的路径还原问题:
最早接触这个问题是在自己学习最短路问题的时候,那个时候学了几个最短路以为自己很牛B了,也是稍微了解了一
下如何在DK最短路算法中还原路径,可是当今天遇到一个BFS需要用到路径还原,真的是绞尽了脑汁2个小时多才把
AC代码给写了出来,这时才认识到自己是有多么的自负,所以决定写下这篇BLOG以供大家学习,也算是自己对知识
点的一个总结吧!
路径还原一直也是最短路问题中的一个常见问题,所以由于某些题的需要,我将在这篇文章中为童鞋们详细讲解这
个问题。
首先,路径还原的意思就是将最短路那条路径记录下来以供使用(如输出最短路的路径)。
这里我将这个问题分的更细,将为大家讲解不同的最短路解法的路径还原方法。我将它们总结为了下面3种:
①最短路算法的路径还原
②DFS求最短路的路径还原
③BFS求最短路的路径还原
另外还会在最后补充路径还原时,如果还需要记录下每次状态转移时的动作时该怎么做以及当点是由(x,y)唯一确定,而不是由单一的编号确定时该怎么记录呢?(看不懂请往后看)
最短路的问题一般都是用这3种方法来求解,其中①仅适用于图的最短路,而DFS和BFS却能求解各种不同形式的最
优解,我们也可以形象的理解为求最短路。下面依次讲解这几个问题:
①最短路算法的路径还原:
理解了最短路算法的童鞋应该明白,这几个算法都是大同小异,其实质上都是贪心的结果,所以每一次松弛操作都是
一次贪心的操作。
在这几个算法中,我们普遍使用辅助数组d[i]来记录i点当前已知(或目前可以求出)的最短路。所以每进行一次松弛
操作都会使d[i]的数值更加接近结果,直到所有松弛操作完成便是最终所求结果,那么我们可以这样想,我们可以使
用另一个辅助数组prv[]来记录当前的最短路路径,具体实现为prv[i]=j代表了i是从j走来的,即当前i的最短路是通过从j
走到i得到的,我们把j叫做i的前驱节点,如此,随着松弛操作的最终完成(也就是最终的最短路的解出),prv[i]=j便
代表着i的真正的最短路是通过从j走到i得到的。另外只要将prv[起点]的值初始化为一个特定值,这里我们用-1就可以
很好的将这条路径输出来了。
vector<int> path; int t = end; //这里end代表终点 for (; t != -1; t = prv[t]) //每次将当前的位置加入path直到到尽头-1,且-1不会被加入path path.push_back(t); reverse(path.begin(), path.end()); //由于path是按从终点到起点的顺序加入的,所以需要反转
觉得这几行代码难以理解的童鞋们可以看如下图片:
看完这幅图估计大家也都能理解了,最短路算法的路径还原就讲到这里。
②DFS求最短路的路径还原
DFS中路径还原是比较麻烦的,到现在我还没有想出有什么好的解决方法,只有特定情况的题我才会还原路径,本来
想上网查查然后给大家分享的,可是一时间也找不到。。。只怪本人能力有限,以后想到了好的方法一定给补上,请
谅解。
只有当DFS搜到的第一条满足条件的路便是所求答案的解时才能直接记录路径(因为找到之后不会再会被其他的走法
覆盖)。
这里我直接给出一个例题,因为也不难,所以也不做过多解释,请大家自行画图(DFS解答树)体会体会。下面给出
这篇文章的链接:点击打开链接
③BFS求最短路的路径还原
BFS与DFS差别最大的一点便是在BFS中每个点(或者说每个状态)都只会入一次队,所以我们仍然可以仿效第①类
的做法,用一个辅助数组prv[]来记录路径,并且prv[i]的值被确定之后就不存在再次被覆盖的可能,因为每个点只会入
一次队嘛,正好我们记录的操作在入队的时候进行。之后的做法就和第①类没有区别了。
然后谈一谈我们在上面遗留的问题,路径还原时,如果还需要记录下每次状态转移时的动作时该怎么做,我的语言表
达能力也不算很强,所以也猜到了很多人会不理解这句话。
举个通俗的例子,假如在移动时(即状态转移时)你可以跳也可以走也可以用跑的,这3种移动法就可以当成是3种不
同的动作,不同的动作会有不同的影响,我们需要记录下的不仅仅是最优解时经过了哪些地点,还要记下每次移动分
别是通过什么样的动作来实现的。再举个例子,在走迷宫时可以往上下左右4个方向走,那么上下左右这4个便是4个
不同的动作。
这时我们便需要prv[i]中记录2个值,一个是前驱节点j,另一个是从j到i的动作。这个问题也很容易解决,只要写个结
构体就行了不是吗?
另一个问题当点是由(x,y)确定,而不是单一的编号确定时该怎么记录,这句话应该比较容易理解,当求城市间的
最短路时,不同的城市肯定是由一个不同的的编号来唯一确定的,可是如果在一个矩阵中,不同的点由一对(x,y)
确定时该怎么办呢?对比prv[i]=j代表j为i的前驱节点,是不是可以将prv定义成一个二维数组(当然前提是不会超过内
存限制),然后在prv[x1][y1]中记录2个值x2,y2,此时就可以很好的做到路径的记录了。
说这么多不如自己去实践一番,这里我仍然给出一个例题,将会用到上面给大家说的技巧:点击打开链接
题解大家可以在我的BFS专题或者本专题里找。