一.基本思想
🧇弗洛伊德算法是一种用于解决所有顶点对最短路径问题的动态规划算法,可以计算有向图或负权图(但不能有负权回路)中所有顶点之间的最短路径。
🧀弗洛伊德算法的核心思想是动态规划,通过逐步考虑中间顶点来优化路径:
1)对于图中的每一对顶点 (i, j),考虑所有可能的中间顶点 k
2)检查是否存在从 i 到 j 的路径经过 k 比已知路径更短
3)如果是,则更新 i 到 j 的最短距离
二、算法特点
🍫时间复杂度:O(n³),其中 n 是顶点数
🍬空间复杂度:O(n²)
🍭可以处理负权边(但不能有负权回路)
🍮能够检测图中是否存在负权回路
🍯同时计算所有顶点对的最短路径
三、算法步骤
🍀1.初始化距离矩阵 D,其中 D[i][j] 表示顶点 i 到 j 的直接距离
1)如果 i == j,D[i][j] = 0
2)如果 i 和 j 之间有边,D[i][j] = 边权
3)否则 D[i][j] = ∞
🍂2.对于每个中间顶点 k(从 1 到 n):
1)对于每对顶点 i 和 j:
2)如果 D[i][k] + D[k][j] < D[i][j]
3)则更新 D[i][j] = D[i][k] + D[k][j]
🍁3.最终 D 矩阵存储了所有顶点对的最短距离
四、典型例题
【问题描述】
对于下面一张若干个城市,以及城市之间距离的地图,请采用弗洛伊德算法求出所有城市之间的最短路径。
【输入形式】
顶点个数n,以及n*n的邻接矩阵,其中不可达使用9999代替
【输出形式】
每两个顶点之间的最短路径和经过的顶点
注意:顶点自身到自身的dist值为0,path则为该顶点的编号
【样例输入】
4
9999 4 11 9999
6 9999 2 9999
1 9999 9999 1
9999 3 9999 9999
【样例输出】
from 0 to 0: dist = 0 path:0
from 0 to 1: dist = 4 path:0 1
from 0 to 2: dist = 6 path:0 1 2
from 0 to 3: dist = 7 path:0 1 2 3
from 1 to 0: dist = 3 path:1 2 0
from 1 to 1: dist = 0 path:1
from 1 to 2: dist = 2 path:1 2
from 1 to 3: dist = 3 path:1 2 3
from 2 to 0: dist = 1 path:2 0
from 2 to 1: dist = 4 path:2 3 1
from 2 to 2: dist = 0 path:2
from 2 to 3: dist = 1 path:2 3
from 3 to 0: dist = 6 path:3 1 2 0
from 3 to 1: dist = 3 path:3 1
from 3 to 2: dist = 5 path:3 1 2
from 3 to 3: dist = 0 path:3
#include<bits/stdc++.h>
using namespace std;
#define max 9999 // 定义无穷大值,表示不可达
int path[100][100]; // 路径矩阵,记录i到j的最短路径中j的前驱节点
int dist[100][100]; // 距离矩阵,记录i到j的最短距离
// 定义图结构
typedef struct graph
{
int arc[100][100]; // 邻接矩阵存储图的边权
int vexnum; // 顶点数量
}Graph;
// 弗洛伊德算法主函数
void floyd(Graph g, int 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++) // 遍历所有终点
{
// 如果通过k节点能使i到j的路径更短
if(dist[i][k] + dist[k][j] < g.arc[i][j])
{
g.arc[i][j] = dist[i][k] + dist[k][j]; // 更新边权
dist[i][j] = g.arc[i][j]; // 更新最短距离
path[i][j] = k; // 记录中间节点
}
}
}
}
// 输出所有顶点对的最短路径信息
for(int i=0; i<n; i++)
{
for(int j=0; j<n; j++)
{
cout << "from " << i << " to " << j << ": dist = " << g.arc[i][j] << " path:";
int c = j; // 当前节点
int p[100]; // 存储路径节点
p[0] = j; // 终点
int x = 0; // 路径节点计数器
if(i == j) // 起点和终点相同
cout << i << endl;
else
{
// 回溯路径:从终点反向查找前驱节点直到起点
while(path[i][c] != i)
{
p[++x] = path[i][c]; // 记录前驱节点
c = path[i][c]; // 移动到前驱节点
}
p[++x] = i; // 添加起点
// 反向输出路径(从起点到终点)
for(; x>=0; x--)
cout << p[x] << ' ';
cout << endl;
}
}
}
}
int main()
{
int n; // 顶点数量
cin >> n;
Graph g;
g.vexnum = n; // 设置顶点数
// 输入邻接矩阵
for(int i=0; i<n; i++)
{
for(int j=0; j<n; j++)
{
cin >> g.arc[i][j]; // 输入边权
if(i == j) // 对角线元素设为0(自己到自己的距离)
g.arc[i][j] = 0;
dist[i][j] = g.arc[i][j]; // 初始化距离矩阵
// 初始化路径矩阵
if(i == j || g.arc[i][j] == max) // 自己到自己的路径或无直接连接
{
path[i][j] = i; // 前驱设为自身
}
else
path[i][j] = i; // 有直接连接时前驱设为起点
}
}
floyd(g, n); // 执行弗洛伊德算法
return 0;
}