Floyd最短路算法
当初学这个算法的时候,都说是DP思想……但是当时学的匆匆,具体是怎么个DP法确实不大清楚,但是理解这个东西,一旦出了偏差,代码就会莫名爆炸……
其实对于最短路的算法,最重要的步骤其实就是松弛,比如Dijkstra算法,贪心策略就是,每次让到源点距离最近的那个点,去松弛其他的点。
而弗洛伊德算法的基操也是松弛操作,但和单源最短路径的只松弛源点到其他点的距离不同,Floyd算法每次选取一个点,去松弛其他任意两点的最短路径
具体过程可以描述为这样:
整个过程可分为n个阶段,n为图中点的个数
在阶段
k
k
k,对于
D
P
[
k
]
[
i
]
[
j
]
DP[k][i][j]
DP[k][i][j],我们可以理解为,已经考虑了前
k
k
k 个点并使用了第
k
k
k 个点做松弛后,点
i
i
i到点
j
j
j的最短距离
则有转移方程:
D
P
[
k
]
[
i
]
[
j
]
=
m
i
n
(
D
P
[
k
]
[
i
]
[
j
]
,
D
P
[
k
−
1
]
[
i
]
[
k
]
+
D
P
[
k
−
1
]
[
k
]
[
j
]
)
DP[k][i][j] = min (DP[k][i][j], DP[k - 1][i][k] + DP[k-1][k][j])
DP[k][i][j]=min(DP[k][i][j],DP[k−1][i][k]+DP[k−1][k][j])
当然,目前看到的算法大多是 D P [ i ] [ j ] = m i n ( D P [ i ] [ j ] , D P [ i ] [ k ] + D P [ k ] [ j ] ) DP[i][j] = min (DP[i][j], DP[i][k] + DP[k][j]) DP[i][j]=min(DP[i][j],DP[i][k]+DP[k][j]),也就是经过了降维。因为对于 D P [ k ] [ i ] [ j ] DP[k][i][j] DP[k][i][j],依赖的历史状态只有 D P [ k − 1 ] [ i ] [ k ] DP[k-1][i][k] DP[k−1][i][k] 和 D P [ k − 1 ] [ k ] [ j ] DP[k-1][k][j] DP[k−1][k][j],如果我们进行降维,只要保证在更新 D P [ k ] [ i ] [ j ] DP[k][i][j] DP[k][i][j]时, D P [ k − 1 ] [ i ] [ k ] DP[k-1][i][k] DP[k−1][i][k] 和 D P [ k − 1 ] [ k ] [ j ] DP[k-1][k][j] DP[k−1][k][j]没有被第K阶段更新过即可。
而只有当 k = i k = i k=i 或 k = j k = j k=j 时, D P [ i ] [ j ] = m i n ( D P [ i ] [ j ] , D P [ i ] [ k ] + D P [ k ] [ j ] ) DP[i][j] = min (DP[i][j], DP[i][k] + DP[k][j]) DP[i][j]=min(DP[i][j],DP[i][k]+DP[k][j])中依赖的两个历史状态可能被第K阶段更新,但是…… k = i k = i k=i 或 k = j k = j k=j的情况,其实就等价于并没有引入第三点,也就是单纯的两点间距离(比如 k = i k = i k=i, 则 D P [ i ] [ j ] = m i n ( D P [ i ] [ j ] , D P [ i ] [ i ] + D P [ i ] [ j ] = D P [ i ] [ j ] ) = D P [ i ] [ j ] , DP[i][j] = min (DP[i][j], DP[i][i] + DP[i][j] = DP[i][j]) = DP[i][j], DP[i][j]=min(DP[i][j],DP[i][i]+DP[i][j]=DP[i][j])=DP[i][j],) ,所以即使降了维,算法还是正确的。
既然K表示状态,所以理应在最外层循环……不要学我这个傻傻的把K写在最内层还对程序WA表示一脸懵逼的傻X
code
for(k = 0; k < n; ++ k) {
for(i = 0; i < n; ++ i) {
for(j = 0; j < n; ++ j) {
if(DP[i][j] > (DP[i][k] + DP[k][j])) {
DP[i][j] = DP[i][k] + DP[k][j];
path[i][j] = k;//顺便可以记录路径
}
}
}
}