算法学习笔记(28)——Floyd算法(多源汇最短路)
Floyd 算法
Floyd 算法用于多源汇最短路问题。时间复杂度为 \(O(n^3)\) 。
算法思想:
首先用邻接矩阵里的d[i][j]存储所有的边(重边的时候取min),然后就是三重循环,思路也是如果从i到k,再从k到j,这个距离(d[i][k] + d[k][j])能比d[i][j]小,就更新一下:d[i][j] = min(d[i][j], d[i][k] + d[k][j])。
需要注意的是三重循环的顺序,先循环中间位置k,再循环源点i,最后循环汇点j。
初始化时,d[i][j]存储的是从i到j的最短边,算法执行结束后d[i][j]就是从i到j的最短距离。
由于存在负权边,如果d[i][j] = INF,d[i][k] = INF,而d[k][j] < 0,那么d[i][j]实际是不可达但也会被更新,所以此时我们不能通过与INF比较来判断是不是可达的,选择一个较大的数(通常是INF/2)进行比较判断,大于该阈值则判为不可达。
Floyd算法属于动态规划算法。
原理分析:
状态表示:f[k][i][j]表示从i点出发,只经过1~k这些中间点,最后到达j点的最短距离。
那么我们就可以根据第k个点选或不选进行状态转移
- 如果不选第
k个点:f[k-1][i][j],表示从i到j经过了1~k-1这些点。 - 如果选第
k个点:只能选择一次,否则最短路存在环。则状态表示为f[k-1][i][k] + f[k-1][k][j],意味着先从i到k经过了1~k-1这些中间点,然后经过1~k-1这些中间点从k到达j。
转移方程可以表示为:
\[f[k][i][j] = min(f[k-1][i][j], f[k-1][i][k] + f[k-1][k][j])
\]
由于转移方程左侧是k,右侧是k-1,所以只要从小到大枚举k即可将这一维度省去,也就演变成了d[i][j]。
#include <iostream>
using namespace std;
const int N = 210, INF = 0x3f3f3f3f;
int n, m, k;
int d[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 ++ )
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}
int main()
{
cin >> n >> m >> k;
// 初始化邻接矩阵
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= n; j ++ )
if (i == j) d[i][j] = 0;
else d[i][j] = INF;
while (m -- ) {
int x, y, z;
cin >> x >> y >> z;
d[x][y] = min(d[x][y], z); //两个点之间只存最短的那条边
}
floyd();
while (k -- ) {
int x, y;
cin >> x >> y;
if (d[x][y] > INF / 2) puts("impossible");
else cout << d[x][y] << endl;
}
return 0;
}

浙公网安备 33010602011771号