[总结] 最短路径数问题
Problem:给定一张n个点m条边的带权有向图,从起点s出发到达终点e,有多少种方法可以走最短的路径到达终点e?
Solution:
首先我们得找到s到各点的单源最短路值(SPFA/Dijkstra)。
问题一,如何确定某个点k是否在s到v的最短路上。这只需要一个判断语句:dist[s][k]+dist[k][v]==dist[s][v]。
问题二,如何从v点出发走最短路径走到点s?找边集(v,k)使得dist[s][k]+edge[k][v]==dist[s][v]。到达点k后,我们再沿k到s的最短路走即可。为什么不是dist[s][k]+dist[k][v]==dist[s][v]?因为若是dist[k][v],k到v的最短路径上可能并非单边,而是多条边和多个点。
知道这些后,我们可以使用搜索遍历每条s到v的最短路(从v出发),然后统计路径的条数加起来。然而这个路径数可能很大,这样子的话就会超时。
那么我们不妨使用记忆化搜索。这样的话这个问题转换成了一个dp方程。
状态:f[i][j] 从i到j的最短路条数值
转移:f[i][j]=sum{f[i][k]} ( (k,j)属于E , d[i][k]+e[k][j]==d[i][j] )
边界:f[i][i]=1
问题得到解决。
然而这玩意会不会有更好的解决方法?到底究竟要不要使用递归?有,可以不用递归。
当我们跑了一边SPFA之后,以s为源,整张图会转换成一个最短路图。点u能向点v连一条有向边的条件为dist[u]+e[u][v]==dist[v]。整张图应该转成了一张DAG(边权为正)。
那么s到一个点k的最短路,也就是f[k],应该从sum{f[j]} ( (j,k)属于E )转换过来,也就是每个出边指向k的点的路径数之和。而若想求得k,每个j都应该求得。
是不是感觉很熟悉?想要求得一个点的信息,必须将所有指向它的点的信息全部求得。这让我们想起了拓扑排序。我们可以使用拓扑排序的顺序对每个点进行处理,由此可以使用非递归求得s到每个点的路径数。
一开始,入度为0的点,也就是点s,显然其f[s]=1.
本质还是dp的过程。