板子:Floyd(每对顶点之间的最短路径)
三重循环,中转点
放在最外层:依次允许经过顶点 中转,看 是否比现在的 更短。
代码
#define INF 0x3f3f3f3f // 「无穷大」:两个 INF 相加也不会超出 int 的范围
void Floyd(MGraph &G, int A[][MAXV], int path[][MAXV]) { // A:最短路径长度;path:中转点,-1 表示直达
int n = G.numVertices;
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++) {
A[i][j] = G.Edge[i][j]; // 初始值:有边就是边的权值,没有边是 INF,对角线是 0
path[i][j] = -1;
}
for (int k = 0; k < n; k++) // 中转点 k 必须放在最外层
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
if (A[i][k] + A[k][j] < A[i][j]) { // 经过 k 中转更短,就更新
A[i][j] = A[i][k] + A[k][j];
path[i][j] = k;
}
}
void printFloydPath(int path[][MAXV], int i, int j) { // 输出 i 到 j 的路径上的中间顶点
int k = path[i][j];
if (k == -1) return; // i 直接到 j,中间没有顶点
printFloydPath(path, i, k); // 先输出 i 到 k 这一段的中间顶点
printf("%d ", k);
printFloydPath(path, k, j); // 再输出 k 到 j 这一段的中间顶点
}复杂度:时间
关键边界
| 位置 | 为什么 |
|---|---|
k 在最外层 | 这是动态规划的阶段。算 k 放到里层结果就错了 |
INF 取 0x3f3f3f3f | 两个 INF 相加约为 int 的上限;取 0x7fffffff 的话,相加会溢出变成负数 |
| 矩阵初始化 | 带权矩阵:没有边存 INF,对角线存 0。和前面几篇的 0/1 矩阵不一样 |
path[i][j] = k 存中转点 | 输出路径时递归拆成 |
易错点
- 允许有负权边,但不允许有负权回路。
- 手算题常考「写出
、 ……」:第 步只用第 行、第 列去更新其他格子,第 行、第 列本身不变。见 6.4.1 Floyd 算法。 - Floyd 也可以用在带权无向图上,把每条无向边看成两条方向相反的有向边。
链接
- 📕 返回:数据结构代码板子
- ⬅️ 上一篇:判断路径、判断有环、判断树
- ➡️ 下一篇:Prim 与 Dijkstra
- 🔗 6.4.1 最小生成树与最短路径