定义图有:n个点,m条边 。
这Floyd算法是处理多源最短路的算法,可以应对边权为负的情况,但不能处理负环。
时间复杂度是 O ( n 3 ) O(n^3) O(n3)
算法步骤
-
f
o
r
(
k
=
1
;
k
<
=
n
;
k
+
+
)
for(k = 1; k <= n; k ++ )
for(k=1;k<=n;k++)
-
f
o
r
(
i
=
1
;
i
<
=
n
;
i
+
+
)
for(i = 1; i <= n; i ++ )
for(i=1;i<=n;i++)
-
f
o
r
(
j
=
1
;
j
<
=
n
;
j
+
+
)
for(j = 1; j <= n; j ++ )
for(j=1;j<=n;j++)
- d [ i , j ] = m i n ( d [ i , j ] , d [ i , k ] + d [ k , j ] ) d[i, j] = min(d[i, j], d[i, k] + d[k, j]) d[i,j]=min(d[i,j],d[i,k]+d[k,j])
-
f
o
r
(
j
=
1
;
j
<
=
n
;
j
+
+
)
for(j = 1; j <= n; j ++ )
for(j=1;j<=n;j++)
-
f
o
r
(
i
=
1
;
i
<
=
n
;
i
+
+
)
for(i = 1; i <= n; i ++ )
for(i=1;i<=n;i++)
算法证明
floyd是基于动态规划的思路实现的。
d
[
k
,
i
,
j
]
d[k, i, j]
d[k,i,j]表示从点
i
i
i出发,只经过
1
,
2
,
.
.
.
,
k
1, 2,...,k
1,2,...,k的中间点,到达
j
j
j的最短距离,设这条路径为
p
p
p。
假设我们已经有了所有的 d [ k − 1 , x , y ] d[k-1, x, y] d[k−1,x,y],现在想要求 d [ k , i , j ] d[k, i, j] d[k,i,j]。
- 结点 k k k不是 p p p上的点,那么 p p p只由 1 , 2 , . . . , k − 1 1,2,...,k-1 1,2,...,k−1这些点组成, d [ k , i , j ] = d [ k − 1 , i , j ] d[k,i,j]=d[k-1,i,j] d[k,i,j]=d[k−1,i,j]
- 结点
k
k
k是
p
p
p上的点,那么可以将路径分解为
i
i
i —
p
1
p_1
p1—>
k
k
k —
p
2
p_2
p2—>
j
j
j
因为 p 1 p_1 p1不含 k k k,所以 p 1 p_1 p1的最短长度是 d [ k − 1 , i , k ] d[k-1, i, k] d[k−1,i,k]。
同理, p 2 p_2 p2的最短长度是 d [ k − 1 , k , j ] d[k-1, k, j] d[k−1,k,j]。
p 1 p_1 p1和 p 2 p_2 p2的最短长度相加就是最短总长度。
综上可以得到递推式:
d
[
k
,
i
,
j
]
=
d
[
k
−
1
,
i
,
k
]
+
d
[
k
−
1
,
k
,
j
]
d[k, i, j] = d[k-1, i, k] + d[k-1, k, j]
d[k,i,j]=d[k−1,i,k]+d[k−1,k,j]
d d d的第一维 k k k可以用滚动数组去掉,只用保留后两维。
由递推顺序,先知道k-1才能知道k,因此k循环放在最外面
算法实现
注意这题边权可能为负,哪怕不存在路径,dis[i]=INF也可能被更新,会比INF更小,不能直接判断dis[i] = INF,可以判断dis[i] > INF / 2输出impossible。
此外该题有自环,因为不存在负环,因此没有负自环,那么走自环肯定不如不走,要将dis[i][i]
置为0。
#include <iostream>
using namespace std;
const int N = 205, M = 20005, INF = 0x3f3f3f3f;
int n, m, k;
int dis[N][N];
void floyd() {
for (int k = 1; k <= n; k ++ ) {
for (int i = 1; i <= n; i ++ ) {
for (int j = 1; j <= n; j ++ ) {
dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
}
}
}
}
int main() {
scanf("%d%d%d", &n, &m, &k);
int u, v, w;
for (int i = 1; i <= n; i ++ ) {
for (int j = 1; j <= n; j ++ ) {
if (i == j) dis[i][j] = 0; // 自环为0
else dis[i][j] = INF;
}
}
while (m --) {
scanf("%d%d%d", &u, &v, &w);
dis[u][v] = min(w, dis[u][v]); // 重边留最小
}
floyd();
while (k -- ) {
scanf("%d%d", &u, &v);
if (dis[u][v] > INF / 2) printf("impossible\n");
else printf("%d\n", dis[u][v]);
}
return 0;
}