多源最短路 Floyd 详解
经典多源最短路 Floyd 算法
/*
* 多源最短路
* 最短路是图论中最为常见的问题之一。按照源点的个数,可以分为单源最短路和多源最短路。
* 在前几篇博客中,我们介绍了单源最短路的 Dijkstra 算法, Bellman-ford 算法, Spfa 算法。复杂度分别为 O(MlogM)、O(NM)、O(最坏NM,平均KM)。
*
* 多源最短路的算法,当然可以通过多次运行单源最短路解决,而且复杂度很低。不过也有几种多源最短路的算法,如 Floyd 最短路算法。
* Floyd 最短路:
* 算法复杂度:
* O(N^3)
* 算法思路:
* for (int k = 1; k <= n; k ++ ) {
* for (int i = 1; i <= n; i ++ ) {
* for (int j = 1; j <= n; j ++ ) {
* dist[i][j][k] = min(dist[i][j][k-1], dist[i][k][k-1] + dist[k][j][k-1]);
* }
* }
* }
* 算法证明:
* dist[u][v][k] 表示 除了 u, v 源点和终点,路径上所经过的点只能为 1, 2, ..., k中的点,在此条件下的最短路径长度。
* 那么,动态规划的递归公式可以为
* dist[u][v][k] = min(dist[u][v][k-1], dist[u][k][k-1] + dist[k][v][k-1]);
* 表示倘若最短路径上加入了 k 点,那么必有 u->k 和 k->v 这样的路径作为支撑。
* 从证明的角度可以看出,Floyd 支持负边,任何最短路都不支持负环。
* 算法巧解:
* 空间优化初级版:
* 可以看出 DP 过程中,可以应用滚动数组求解。 空间复杂度 O(N^2)。
* 空间优化进阶版:
* 关键点在于 dist[i][k][k]、dist[k][j][k] 在 k 这个循环中,永远不会更新
* 也就是说 dist[i][k][k] = dist[i][k][k - 1],dist[k][j][k] = dist[k][j][k - 1],因为 k 点的加入不会影响。
* 因为我们完全可以 忽略第三维度!!!
* for (int k = 1; k <= n; k ++ )
* for (int i = 1; i <= n; i ++ )
* for (int j = 1; j <= n; j ++ )
* dist[i][j][k] = min(dist[i][j][k-1], dist[i][k][k-1] + dist[k][j][k-1]);
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <vector>
using namespace std;
const int N = 210, INF = 0x3f3f3f3f;
int n, m, q;
int dist[N][N];
int main()
{
scanf("%d%d%d", &n, &m, &q);
memset(dist, 0x3f, sizeof dist);
for (int i = 1; i <= n; i ++ ) {
dist[i][i] = 0;
}
int a, b, c;
while (m -- ) {
scanf("%d%d%d", &a, &b, &c);
dist[a][b] = min(dist[a][b], c);
}
for (int k = 1; k <= n; k ++ ) {
for (int i = 1; i <= n; i ++ ) {
for (int j = 1; j <= n; j ++ ) {
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
}
}
}
while (q -- ) {
scanf("%d%d", &a, &b);
if (dist[a][b] >= INF / 2) {
printf("impossible\n");
} else {
printf("%d\n", dist[a][b]);
}
}
return 0;
}