之前有了单源最短路的基本算法,如果我们希望求每对顶点间的最短路径,可以把单源路径重复运用,也可以整体用动态规划的思路求解。
提出两个动态规划的思路:按经过边的数量考虑(最多经过v-1条边),按增加一条边构造递推公式;按经过中间点的集合考虑(最多经过所有点),按增加一个点构造递推公式。
其实我们可以感性地看出,后一种思路更优,因为它递推中隐含着边的数量(加一个点则最多经过的边的数量也加一)同时还具体到了哪些点(也就具体到了哪些边),也就是说这个操作过程更精细。最后事实上从复杂度上讲后一种也更优,前一O(V4)即使用了重复平方技术也只能到O(V3lgV),后一种O(V3)。
给出伪码:
1:
SLOW-ALL-PAIRS-SHORTEST-PATHS(W)
n <- row[W]
L[1] <- W //only need to be L
for m <- 2 to n-1
do L[m] <- EXTEND-SHORTEST-PATHS(L[m-1],W) // L <- EXTEND-SHORTEST-PATHS(L,W)
return L[n-1] //L
EXTEND-SHORTEST-PATHS(L,W)
n <- rows[L]
let L' = (l'[i][j]) bean n*n matrix
for i <- 1 to n
do for j <- 1 to n
do l'[i][j] <- inf // infinity
for k <- 1 to n
do l'[i][j] <- min(l'[i][j],l[i][k] + w[k][j])
return L'
2(Floyd-Warshall):
FLOYD-WARSHALL(W)
n <- rows[W]
D[0] <- W //only need to be D
for k <- 1 to n
do for i <- 1 to n
do for j <- 1 to n
do d[k][i][j] <- min(d[k-1][i][j],d[k-1][i][k] + d[k-1][k][j]) // d[i][j] <- min(d[i][j],d[i][k]+d[k][j])
return D[n] // D
对于求每对顶点的最短距离而言,这个代码简单复杂度也可以。而每对顶点的最短路包含两个点之间的连通性,我们可以借此求解传递闭包,当然如果我们只要求解闭包而不考虑距离大小,可以用bit做数据结构。
同时,重复用单源路径,在稀疏图上可以做到复杂度O(V2lgV+VE),即为Johnson算法,先用 Bellman-Ford检查负权回路,对于没有负权回路的运用重赋权技术,将权全部变为非负,就可以重复用较快的 Dijkstra。
里面在算重赋权的时候,提出一个虚拟的点,到所有点有一个权为0的边,这样来算各个顶点映射的值保证非负(不证明了。。。)。这是一个很有意思的思想,我们可以借鉴用来处理其他一些问题。比如有很多网络上有很多点是服务器,其他点是用户,要找用户到最近服务器的距离,我们可以提出一个虚拟的点到所有的服务器,权为0,这样以虚拟点做单源最短路,就不需要求所有点的最短路。也就是构图的时候可以考虑为一类点构造一个虚拟的点,将问题化简。