简介
Floyd-Warshall算法是求解多源最短路的算法之一,核心思想是动态规划,适用于不包含负环(负权回路)的图。时间复杂度\(O(n^{3})\),空间复杂度\(O(n^{2})\)。
Floyd-Warshall算法
对于任何一个点而言,i到j的最短距离不外乎i到j经过k
和不经过k
两种可能,所以可以令\(k=1,2,3,\ldots,n\),检查\(dis(i, j)\)与\(dis(i, k)+dis(k, j)\)的大小,在此\(dis(i, k)\)与\(dis(k, j)\)分别是目前为止所知的i到k与k到j的最短距离,因此\(dis(i, k)+dis(k, j)\)就是i到j经过k的最短距离。
所以,若有\(dis(i, j) \gt dis(i, k)+dis(k, j)\),就表示从i出发经过k再到j的距离要比原来的i到j距离短,自然把i到j的距离\(dis(i, j)\)更新为\(dis(i, k)+dis(k, j)\),更新完后\(dis(i, j)\)就是目前的i到j的最短距离。重复这一过程,最后当遍历完k时,\(dis(i, j)\)里面存放的就是i到j之间的最短距离了。
我们定义数组\(dp[k][x][y]\)表示只允许经过结点1到k,结点x到结点y的最短路径长度,那么状态转移方程就是\(dp[k][x][y] = min(dp[k-1][x][y], dp[k-1][x][k]+dp[k-1][k][y])\),可以发现第一维是没有用的,于是直接改成\(dp[x][y] = min(dp[x][y], dp[x][k]+dp[k][y])\)。
代码实现很简单,不过要注意循环的嵌套循序,k作为阶段,枚举时的循环要放在最外层:
1 2 3 4 5 6 7
| void floyd(int n) { for(int k=1; k<=n; k++) for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) g[i][j] = min(g[i][j], g[i][k]+g[k][j]); }
|
*传递闭包
Floyd算法不仅可以求最短路,也可以维护关系求传递闭包。建立邻接矩阵\(f[i][j]\)表示i和j是否有关系,跑一遍floyd,然后检查\(f[i][j]\&f[j][i]\)是否为1。下面给出bitset优化版本,时间复杂度\(O(\frac{n^{3}}{w})\)。
1 2 3 4 5 6 7
| void floyd(int n) { for(int k=1; k<=n; k++) for(int i=1; i<=n; i++) if(f[i][k]) f[i] = f[i] | f[k]; }
|
模板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| const int inf = 0x3f3f3f3f; const int mxn = 1e3 + 5;
int g[mxn][mxn], pre[mxn][mxn];
void graph_init(int n) { for(int i=1; i<=n; i++){ for(int j=1; j<=n; j++) g[i][j] = inf; g[i][i] = 0; } }
void floyd_init(int n) { for(int i=1; i<=n; i++){ for(int j=1; j<= n; j++){ pre[i][j] = (g[i][j]==inf ? -1 : j); } } }
void floyd(int n) { for(int k=1; k<=n; k++) for(int i=1; i<=n; i++) for(int j=1; j<=n; j++){ if(g[i][k]+g[k][j] < g[i][j]){ g[i][j] = g[i][k]+g[k][j]; pre[i][j] = pre[i][k]; } } }
int main() { int n, m; scanf("%d %d", &n, &m); graph_init(n);
for(int i=0; i<m; i++) { int u, v, w; scanf("%d %d %d", &u, &v, &w); g[u][v] = w; } floyd_init(n); floyd(n);
for(int i=1; i<=n; i++){ for(int j=1; j<=n; j++) printf("%d ", g[i][j]); printf("\n"); }
for(int i=1; i<=n; i++){ for(int j=1; j<=n; j++) printf("%d ", pre[i][j]); printf("\n"); }
return 0; }
|