最短路算法:Floyd-Warshall算法学习笔记
1. 算法简介
Floyd-Warshall算法,又称为插点法,是一种用于求解任意两点间的最短路径的算法,同时也可以用于检测图中是否存在负权环。
相对于Dijkstra算法和Bellman-Ford算法,Floyd-Warshall算法可以处理边权值为负数的情况,在解决同一问题时时间复杂度更低。
2. 算法思路
Floyd-Warshall算法的思路非常简单,可以用动态规划的思想来理解。
对于图中的任意两个顶点i和j,只考虑它们之间的k个中间顶点,假设这k个顶点分别是1,2,…,k。那么,i到j的最短路径就可以分成两部分:
- i到1的最短路径,加上1到j的最短路径
- i到2的最短路径,加上2到j的最短路径
… - i到k的最短路径,加上k到j的最短路径
其中,每一步都是在原来的路径上添加了一个新的中间顶点。因此,我们可以用一个二维数组d[i][j]来记录任意两点之间的最短路径长度。
为了实现这个算法,我们需要在计算每对顶点之间的最短路径时,考虑所有可能的中间顶点。因此,我们要遍历0到n-1这n个顶点,对于每个顶点k,我们都要计算一次从i到j的最短路径长度。这个过程可以用下面的代码来实现:
for (int k = 0; k < n; k++)
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
其中,n是图中顶点的个数,d[i][j]表示从顶点i到顶点j的最短路径长度,初始值是i到j的边权值,如果没有直接相连的边,则赋值为无穷大。
3. 算法性质
- 对于所有的i和j,d[i][j]表示从顶点i到顶点j的最短路径长度。
- 如果图中存在负权环,则算法会失败,因为我们无法确定一个负权环上的顶点的最短路径长度。
4. 算法优化
- 空间优化:由于每个状态只与前一个状态有关(即每次更新d[i][j]时只需要用到上一次更新的值),因此可以将二维数组d改成一维数组。
- 提前退出:如果在更新d[i][j]时,发现d[i][k]+d[k][j]>=d[i][j](其中k为任意中间顶点),那么就可以提前退出当前循环,因为已经不能更新d[i][j]的值了。
5. C++代码实现
下面是Floyd-Warshall算法的C++实现代码:
const int INF = 0x3f3f3f3f;
const int N = 1005;
int n, m;
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() {
memset(d, INF, sizeof(d));
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int x, y, w;
cin >> x >> y >> w;
d[x][y] = w;
}
Floyd();
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
if (i != j && d[i][j] != INF)
cout << "从" << i << "到" << j << "的最短路径长度是:" << d[i][j] << endl;
return 0;
}
6. 总结
Floyd-Warshall算法是一种常用的图论算法,能够高效地计算任意两点之间的最短路径。与Dijkstra算法和Bellman-Ford算法相比,它可以处理边权为负数的情况,在解决同一问题时时间复杂度更低,因此应用范围更广。我们需要掌握Floyd-Warshall算法的基本思想和优化方法,并理解其性质和局限性,才能更好地使用它解决实际问题。