
Warning: Undefined variable $page_id in /www/wwwroot/www.lxwow.top/wp-content/themes/zibll1/functions.php on line 78
一、核心定义
图论最短路问题,是在带权图中寻找两个节点之间路径总权值最小的路径,权值可代表距离、时间、成本等任意可累加的度量维度,是图论领域的经典基础问题。
二、主流分类
1.单源最短路:从一个固定起点出发,求解其到图中所有其他节点的最短路径,是导航软件的核心算法逻辑。
2.多源最短路:直接计算图中任意两个节点之间的最短路径,常用于全局交通网络的路径规划。
3.扩展类型:包含分层图最短路、同余最短路等特殊变种,可处理带通行限制、数学约束的复杂路径场景。
三、主流经典算法
| 算法名称 | 适用场景 | 核心特点 |
|---|---|---|
| Dijkstra | 无负权边的图 | 基于贪心策略,是最常用的单源最短路算法,堆优化后效率大幅提升 |
| Bellman-Ford | 含负权边但无负环的图 | 可检测图中的负环,SPFA是其队列优化版本,大幅降低时间复杂度 |
| Floyd-Warshall | 全局多源最短路计算 | 动态规划实现,代码简洁,适合稠密图的全节点最短路径求解 |
| BFS/DFS | 无权图或等权图 | 实现简单,可快速求解两点间的最短步数路径 |
深入 – Dijkstra算法
Dijkstra算法是求解无负权边带权图的单源最短路径的经典算法,核心基于贪心策略,从源点出发逐步确定所有节点的最短路径,以下是完整的具体计算步骤:
一、前置准备
1.定义核心数组
dist[]:记录源点到每个节点的当前最短距离,初始时源点到自身距离设为0,其余节点设为无穷大(表示暂时不可达)。
visited[]:布尔数组,标记节点是否已被确定最短路径,初始全为False。
(可选)pre[]:记录每个节点在最短路径上的前驱节点,用于后续还原完整路径。
2.明确约束条件:该算法仅适用于无负权边的图,若图中存在负权边,贪心策略会失效,无法得到正确结果。
二、核心计算步骤
1.初始化操作 将源点的dist值设为0,其余节点dist值设为无穷大,visited数组全部置为未访问状态。
2.选取当前最优节点 遍历所有未被访问的节点,找到dist值最小的节点u,该节点就是当前距离源点最近的未确定节点。
3.标记已确定状态 将节点u的visited值设为True,代表该节点的最短路径已被最终确定,后续不会再更新。
4.执行松弛更新 遍历节点u的所有邻接节点v,若v未被访问,且满足dist[u] + u到v的边权 < dist[v],则更新dist[v] = dist[u] + u到v的边权,同时将pre[v]设为u,记录路径前驱。
5.循环终止条件 重复执行步骤2-4,直到所有节点都被标记为已访问,此时dist数组中存储的就是源点到所有节点的最终最短距离。
三、完整示例演示
以源点D为例,求解其到其余节点的最短路径过程如下:
1.初始化:dist[D]=0,其余节点距离为无穷大,已访问集合为{D}。
2.第一轮选邻接节点中距离最小的C(距离3),标记为已访问,更新其邻接节点B、F的距离。
后续依次选取E,F,G,B,A,重复标记和松弛操作,最终得到D到所有节点的最短距离,比如D到A的最短距离为22,路径为D→E→F→A.
对比 Dijkstra 算法的朴素版与堆优化版。理解这两者的区别,关键在于明白算法中“寻找当前距离最小节点”这一步的效率瓶颈。以下是两种实现的详细对比、代码实现及适用场景分析。
四、核心差异对比
| 特性 | 朴素版 Dijkstra | 堆优化版 Dijkstra |
|---|---|---|
| 数据结构 | 邻接矩阵 (或邻接表) + 数组 | 邻接表 + 优先队列 (最小堆) |
| 找最小值方式 | 线性扫描所有未访问节点 O(V) | 从堆顶直接获取O(1),调整堆O(logV) |
| 时间复杂度 | O(V2) | O(ElogV) |
| 空间复杂度 | O(V2) (矩阵) 或O(V+E) | O(V+E) |
| 适用图类型 | 稠密图 (边数 E 接近 V2) | 稀疏图 (边数 E 远小于 V2) |
| 节点处理次数(每个节点仅被确定一次) | 节点可能多次入堆,但只处理最新距离那次 |
注:全文中V 为顶点数 (Vertices),E 为边数 (Edges)。
代码实现 (Java/C++ 风格伪代码逻辑)
1. 朴素版 (适合小规模/稠密图)
核心逻辑:每轮循环暴力遍历 dist 数组,找到未访问且距离最小的点。
// 假设使用邻接矩阵 graph[V][V],INF 表示无穷大
int[] dist = new int[V];
boolean[] visited = new boolean[V];
Arrays.fill(dist, INF);
dist[start] = 0;
for (int i = 0; i < V; i++) {
// 1. 线性查找未访问节点中 dist 最小的 u
int u = -1;
int minDist = INF;
for (int j = 0; j < V; j++) {
if (!visited[j] && dist[j] < minDist) {
minDist = dist[j];
u = j;
}
}
if (u == -1) break; // 剩余节点不可达
visited[u] = true; // 标记为已确定
// 2. 松弛操作:更新 u 的所有邻接点
for (int v = 0; v < V; v++) {
if (!visited[v] && graph[u][v] != INF) {
if (dist[v] > dist[u] + graph[u][v]) {
dist[v] = dist[u] + graph[u][v];
}
}
}
}
2. 堆优化版 (适合大规模/稀疏图)
核心逻辑:使用优先队列(最小堆)自动维护当前距离最小的节点。注意“惰性删除”技巧:如果取出的节点距离大于当前记录的最短距离,说明是旧数据,直接跳过。
// 假设使用邻接表 adj: List<List<int[]>>,int[] 为 {neighbor, weight}
int[] dist = new int[V];
Arrays.fill(dist, INF);
dist[start] = 0;
// 优先队列:存储 {距离, 节点ID},按距离从小到大排序
PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> a - b);
pq.offer(new int[]{0, start});
while (!pq.isEmpty()) {
int[] curr = pq.poll();
int d = curr;
int u = curr;
// 惰性删除:如果当前取出的距离比已知最短距离大,说明是过时数据,跳过
if (d > dist[u]) continue;
// 遍历 u 的所有邻接边
for (int[] edge : adj.get(u)) {
int v = edge;
int w = edge;
// 松弛操作
if (dist[v] > dist[u] + w) {
dist[v] = dist[u] + w;
// 将更新后的节点加入堆(允许重复入堆,靠上面的惰性删除处理)
pq.offer(new int[]{dist[v], v});
}
}
}
上面2个代码是Java的,不要抄(抄的是gay)
为什么堆优化版更快?
1.瓶颈突破:朴素版的瓶颈在于每次都要花 O(V) 的时间去“找最小值”。当图很大时(例如 V=10^5),V^2 的运算量高达 10^10,必然超时。
2.堆的优势:堆优化版将“找最小值”降低到 O(logV)。虽然每个节点可能多次入堆,但总操作次数与边数 E 相关。对于稀疏图(E≈V),复杂度约为O(VlogV),远优于O(V^2)。
3.惰性删除:堆不支持直接修改内部元素的值。因此,当发现更短路径时,我们不是修改堆中旧元素,而是直接插入一个新元素。旧元素会在后续被弹出时通过 if (d > dist[u]) continue; 被忽略。
选朴素版
-
节点数 V≤1000 左右。
-
图非常稠密(几乎每两个点之间都有边)。
-
代码实现简单,不易出错,适合面试手写或小型项目。
选堆优化版
-
节点数 V>1000,甚至达到 10^5 或 10^6。
-
图比较稀疏(如地图导航、社交网络关系)。
-
对性能要求高,是工程实践和算法竞赛的主流选择。
深入 – Bellman-Ford 算法
Bellman-Ford 是解决单源最短路径问题的基础算法,其核心优势在于能处理负权边并检测负权环,且天然支持限制路径边数/节点数的场景。
1. 核心原理
基于动态规划思想,通过反复对图中所有边进行“松弛”操作,逐步逼近最短路径。
-
松弛操作:对于每条边 u→v,权重为 w,如果
dist[u] + w < dist[v],则更新dist[v]。 -
迭代次数:在一个有 V 个节点的图中,若无负权环,最短路径最多包含 V−1 条边。因此,只需进行 V−1 轮对所有边的松弛,即可保证找到所有节点的最短路径。
2. 具体计算步骤
1.初始化:源点距离设为 0,其余节点设为无穷大。
2.循环松弛:执行 V−1 次循环。在每次循环中,遍历图中的每一条边,尝试更新终点的最短距离。
3.负环检测(可选):再进行一次全边遍历,若仍有节点距离能被更新,说明图中存在负权环(因为正常最短路径在V−1轮后应已收敛)。
3. 特殊应用:限制经过节点/边数
这是 Bellman-Ford 独有的优势。若题目要求“从起点到终点最多经过k个节点”(即最多k+1条边),只需将上述第 2 步的循环次数从V−1改为k+1即可。
示例
求从节点 1 到节点 4 最多经过 1 个节点(即最多 2 条边)的最短距离。
-
第 1 轮松弛:求出从起点出发经过 1 条边可达节点的最短距离。
-
第 2 轮松弛:求出从起点出发经过 2 条边可达节点的最短距离。
-
此时
dist即为所求结果。即使图中存在更短但经过更多节点的路径(如经过负权边形成的长路径),算法也会因循环次数限制而忽略它。
4. 复杂度分析
-
时间复杂度:O(V⋅E),其中 V 为节点数,E 为边数。
-
缺点:效率较低,每轮都盲目遍历所有边,包含大量无效松弛。
深入 – SPFA 算法(Bellman-Ford 的队列优化)
SPFA (Shortest Path Faster Algorithm) 是 Bellman-Ford 的改进版本,由段凡丁于 1994 年提出。它通过队列机制避免了不必要的松弛操作,在实际应用中(尤其是稀疏图)效率远高于朴素 Bellman-Ford。
1. 核心优化思想
-
动态逼近:只有当某个节点 u 的最短距离被更新时,由 u 出发的边才可能导致其邻接节点 v 的距离更新。
-
队列维护:使用一个先进先出(FIFO)队列来存储“待处理”的节点。初始时只有源点在队列中。每次取出队首节点进行松弛,若邻接节点距离被更新且不在队列中,则将其入队。
2. 具体计算步骤
1.初始化:dist 数组初始化为无穷大,源点为 0。创建队列,将源点入队,并标记源点在队列中。
2.循环处理
当队列非空时:
取出队首节点 u,标记其不在队列中。
遍历 u 的所有出边 (u,v,w)。
松弛判断:
若dist[u] + w < dist[v]更新 dist[v] = dist[u] + w。
若 v 不在队列中,则将 v 入队,并标记其在队列中。
3.负环检测:记录每个节点入队次数。若某节点入队次数超过 V 次,则说明存在负权环。
3. 性能与适用性
-
平均时间复杂度:O(k⋅E),其中 k 为节点平均入队次数。在随机稀疏图中,k 通常很小(约 2-3),效率接近 O(E)。
-
最坏时间复杂度:O(V⋅E),与 Bellman-Ford 相同。某些刻意构造的数据(如网格图或特定链式结构)会导致 SPFA 退化。
-
适用场景
1.图中含有负权边。
2.需要检测负权环。
3.稀疏图(E 远小于 V^2).
在无负权边的图中,Dijkstra 算法(堆优化)的效率始终高于或等于 SPFA,因此正权图首选 Dijkstra。
| 场景特征 | 推荐算法 | 理由 |
|---|---|---|
| 无负权边 | Dijkstra (堆优化) | 效率最高,稳定 O(ElogV),SPFA 在正权图上可能退化且常数较大。 |
| 有负权边,无负环 | SPFA | 能处理负权,且在稀疏图中平均效率远优于朴素 Bellman-Ford。 |
| 需检测负环 | SPFA 或 Bellman-Ford | 两者均可,SPFA 实现更简洁,通过入队次数判断;Bellman-Ford 通过第V轮是否更新判断。 |
| 限制路径边数/节点数 | Bellman-Ford | 天然支持通过控制松弛轮数来限制路径长度,SPFA 难以直接实现此限制。 |
| 稠密图且含负权 | Bellman-Ford | 若图非常稠密,SPFA 的队列开销可能使其优势不明显,朴素Bellman-Fird代码更简单稳定。 |
Dijkstra 是正权图的最优解,贪心策略保证了高效性。
Bellman-Ford 是处理负权和路径限制的基础,虽然慢但功能强大且逻辑直观。
SPFA 是 Bellman-Ford 的工程优化版,在大多数含负权的实际场景(如网络路由、金融套利检测)中表现优异,但需注意其在极端数据下的稳定性。
再深入 – Bellman-Ford解决路径长度受限
在普通的最短路问题中,我们通常不关心路径经过了多少条边。但在某些场景(如航班中转限制、物流层级配送)中,必须严格限制路径长度(边数)。
1. 核心难点:防止“串联更新”
如果在同一轮迭代中,直接使用当前轮次刚刚更新过的 dist 值去更新后续节点,会导致“一步走多段”的现象。
错误示例
假设限制最多 1 条边。
-
先更新
dist[B] = dist[A] + w(A,B)。 -
紧接着用新的
dist[B]去更新dist[C] = dist[B] + w(B,C)。 -
结果:
dist[C]实际上经过了 A->B->C 两条边,违反了“最多 1 条边”的限制。
2. 解决方案:备份数组 (Backup)
为了解决上述问题,每一轮迭代开始前,必须复制一份上一轮结束时的 dist 数组作为备份 (backup)。在松弛操作中,读取使用 backup,写入更新 dist。这样能保证每一轮迭代只向外扩展一层。
3. 代码实现 (C++)
#include <bits/stdc++.h>
using namespace std;
const int N = 510, M = 10010;
const int INF = 0x3f3f3f3f;
int n, m, k; // n:点数, m:边数, k:限制边数
int dist[N], backup[N]; // dist:当前最短距离, backup:上一轮的距离备份
struct Edge {
int a, b, w; // 起点a, 终点b, 权重w
} edges[M];
void bellman_ford() {
memset(dist, 0x3f, sizeof dist);
dist = 0; // 假设从1号点出发
// 外层循环:限制最多经过 k 条边
for (int i = 0; i < k; i++) {
// 【关键步骤】备份上一轮的状态,防止串联更新
memcpy(backup, dist, sizeof dist);
// 内层循环:遍历所有边进行松弛
for (int j = 0; j < m; j++) {
int a = edges[j].a, b = edges[j].b, w = edges[j].w;
// 注意:这里读取的是 backup[a],而不是 dist[a]
if (backup[a] != INF && dist[b] > backup[a] + w) {
dist[b] = backup[a] + w;
}
}
}
}
int main() {
cin >> n >> m >> k;
for (int i = 0; i < m; i++) {
int a, b, w;
cin >> a >> b >> w;
edges[i] = {a, b, w};
}
bellman_ford();
// 判断是否可达
// 注意:由于存在负权边,不可达点的dist可能不是INF,而是INF减去某个值
// 所以通常判断 dist[n] > INF / 2
if (dist[n] > INF / 2)
cout << "impossible" << endl;
else
cout << dist[n] << endl;
return 0;
}
再深入 – SPFA解决路径无限缩小
SPFA 不仅可以求最短路,还是检测图中是否存在负权环的高效工具。如果存在负权环,最短路径将无限小(无解)。
1. 检测原理
-
最短路径性质:在一个没有负权环的图中,任意两点间的最短路径最多包含 N−1 条边(N 为节点数)。
-
判据:如果在 SPFA 过程中,某个节点被松弛(更新距离)的次数导致其路径上的边数≥N,则说明路径中必然存在环。由于该环能让路径更短,它一定是负权环。
2. 实现技巧:cnt 数组
维护一个 cnt[] 数组,cnt[x] 表示从源点到节点 x 的当前最短路径所经过的边数。
-
当
dist[j] > dist[t] + w时,不仅更新dist[j],还要更新cnt[j] = cnt[t] + 1。 -
若
cnt[j] >= n,则直接返回true(存在负环)。
3. 代码实现 (通用版)
为了检测整个图(包括不连通部分)是否存在负环,通常引入一个超级源点,或者初始时将所有节点入队。
#include <bits/stdc++.h>
using namespace std;
const int N = 2005, M = 10005;
const int INF = 0x3f3f3f3f;
int n, m;
int h[N], e[M], w[M], ne[M], idx;
int dist[N], cnt[N]; // cnt[i]记录从源点到i的路径边数
bool st[N]; // 记录节点是否在队列中
void add(int a, int b, int c) {
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
// 返回 true 表示存在负环,false 表示不存在
bool spfa_check_negative_cycle() {
queue<int> q;
// 【关键步骤】将所有节点入队,并标记为在队列中
// 这样可以检测图中所有连通分量的负环,而不仅仅是从起点可达的部分
for (int i = 1; i <= n; i++) {
q.push(i);
st[i] = true;
}
while (!q.empty()) {
int t = q.front();
q.pop();
st[t] = false;
for (int i = h[t]; i != -1; i = ne[i]) {
int j = e[i];
if (dist[j] > dist[t] + w[i]) {
dist[j] = dist[t] + w[i];
cnt[j] = cnt[t] + 1; // 更新边数
// 【核心判据】如果边数 >= n,说明存在负环
if (cnt[j] >= n)
return true;
if (!st[j]) {
q.push(j);
st[j] = true;
}
}
}
}
return false;
}
int main() {
cin >> n >> m;
memset(h, -1, sizeof h);
for (int i = 0; i < m; i++) {
int a, b, c;
cin >> a >> b >> c;
add(a, b, c);
}
if (spfa_check_negative_cycle())
cout << "Yes" << endl; // 存在负环
else
cout << "No" << endl; // 不存在负环
return 0;
}
对比
| 特性 | Bellman-Ford (限制边数) | SPFA (检测负环) |
|---|---|---|
| 核心数据结构 | backup 数组 |
cnt 数组 + 队列 |
| 关键逻辑 | 每轮迭代前复制 dist 到 backup,松弛时读 backup 写 dist |
更新距离时同步更新 cnt,若 cnt >= n 则判定有负环 |
| 适用场景 | 航班中转限制、分层网络路由、有步数限制的游戏地图 | 金融套利检测、判断系统稳定性、含负权图的可行性分析 |
| 时间复杂度 | O(k⋅m) | 平均 O(m),最坏 O(n⋅m) |
深入 – Floyd算法
一、核心定位
Floyd算法可以一次性求出图中所有节点对之间的最短路径,无需像多次调用单源算法那样重复计算,基于动态规划思想实现,代码逻辑十分简洁。
二、核心原理
它以邻接矩阵为基础载体,依次让每个节点尝试作为“中间中转节点”,判断是否能通过该节点得到更短的路径:
若dist[i][k] + dist[k][j] < dist[i][j],就更新i到j的最短距离为这个更小值。
同时配套维护一个前驱矩阵,用来记录路径的中转信息,最终可以反向还原出完整的最短路径。
三、关键特性
时间复杂度为O(V³),空间复杂度为O(V²),更适合节点数不多的稠密图场景。
支持处理带负权边的图,但无法正确处理存在负权环的图,算法执行前需要先校验图中是否存在负环。
相比多次调用Dijkstra,它的实现成本极低,仅需几行核心代码即可完成全图最短路计算。
深入 – Floyd算法
Floyd-Warshall 算法(简称 Floyd 算法)是图论中解决多源最短路径问题(All-Pairs Shortest Path, APSP)的经典动态规划算法。它能一次性计算出图中任意两个节点之间的最短距离。
以下是关于 Floyd 算法的深度解析,包括其核心原理、与 Dijkstra 的对比、代码实现及工程选型建议。
一、核心原理:动态规划的三重循环
Floyd 算法的核心思想非常直观:如果从节点 i 到节点 j 经过中间节点 k 的路径比直接路径更短,则更新最短距离。
1. 状态定义
设 distdis**t 为节点 ii 到节点 jj 的最短路径长度。
2. 状态转移方程
dist[i][j]=min(dist, dist[i][k]+dis*t) 其中 k 是枚举的“中转点”。算法通过逐步增加允许使用的中转点集合 {1,2,…,k},最终得到允许使用所有节点作为中转点时的全局最短路径。
3. 执行逻辑
算法包含三层嵌套循环:
最外层:枚举中转点k(从 1 到 V)。
中间层:枚举起点 i(从 1 到 V)。
最内层:枚举终点 j(从 1 到 V)。
注意:循环顺序必须是K->I->J。这是因为在计算dist[i][j] 时,我们需要确保 dist[i][k] 和dist 已经是基于前k−1个中转点计算出的最优解。
二、Floyd vs. 多次调用 Dijkstra
当需要求所有节点对的最短路径时,我们可以选择运行 V 次 Dijkstra,或者运行1次 Floyd。两者的对比如下:
| 维度 | Floyd-Warshall 算法 | V 次 Dijkstra (堆优化) |
|---|---|---|
| 时间复杂度 | O(V3) | O(V⋅ElogV)(稀疏图约 O(V2logV))(稠密图约O(V3logV)) |
| 空间复杂度 | O(V2) (需存储邻接矩阵) | O(V+E) (邻接表) |
| 负权边支持 | 支持 (只要无负权环) | 不支持 (贪心策略失效) |
| 代码复杂度 | 极低 (核心仅 5 行代码) | 较高 (需实现优先队列、松弛逻辑) |
| 适用场景 | 稠密图、小规模图、需处理负权、多源查询频繁 | 稀疏图、大规模图、单源查询为主 |
关键结论:
-
稠密图优势:当图非常稠密(边数E≈V2)时,V 次 Dijkstra 的复杂度约为O(V3logV),而 Floyd 仅为O(V3)。此时 Floyd 不仅代码更简洁,常数因子更小,实际运行往往更快。
-
负权边唯一解:如果图中存在负权边但没有负权环,Dijkstra 无法使用,而 Floyd 可以直接处理。
-
内存瓶颈:Floyd 需要O(V2) 的矩阵空间。当V>10000时,内存占用可能达到数百 MB 甚至 GB 级别,此时通常不再适用 Floyd。
三、C++ 实现
void floyd(vector<vector<int>>& dist) {
int n = dist.size();
for (int k = 0; k < n; ++k) {
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
// 安全加法,防止整数溢出
if (dist[i][k] != INF && dist[k][j] != INF) {
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
}
}
}
}
}
四、路径还原与负环检测
1. 路径还原
如果需要知道具体的路径(而不仅仅是距离),可以维护一个前驱矩阵 next_node[i][j],表示从 i 到j 的最短路径上,i 的下一个节点。
-
初始化:若 i 到 j 有直接边,
next_node[i][j] = j,否则为 -1。 -
更新:当
dist[i][j]被dist[i][k] + dist[k][j]更新时,令next_node[i][j] = next_node[i][k]。 -
回溯:从 i 开始,不断查找
next_node直到到达j。
2.负权环检测
Floyd 算法执行完毕后,检查对角线元素 dist[i][i]。
-
若存在某个i使得
dist[i][i] < 0,则说明图中存在经过节点i的负权环。 -
因为正常情况下,节点到自身的距离应为 0。如果小于 0,说明绕了一圈回来距离变小了。
五、工程选型指南
在实际开发中,如何选择最短路算法?请参考以下决策树:
1.查询需求是什么?
单源查询(如导航从 A 到 B):首选 Dijkstra (堆优化) 或 A。
多源查询(如社交网络中任意两人的关系度):进入下一步判断。
图的规模与密度如何?
节点数V<500:无脑选 Floyd。代码简单,不易出错,速度足够快。
节点数V>1000 且图稀疏:选 V次 Dijkstra。内存更省,速度更快。
节点数极大(V=10^5):Floyd 不可用。若需多源,考虑近似算法或特定场景优化(如 landmarks 预处理)。
2.是否存在负权边?
有负权不能用 Dijkstra。
若V 小:用 Floyd。
若V 大且稀疏:用 SPFA 或 Bellman-Ford
你学会了吗?
尝试一下
图论 – 最短路练习
来源(我就不具体写了,复制粘贴查一下即可):
1.https://wenxin.baidu.com/?extParams=%7B%22enter_type%22%3A%22home_operate%22%7D
2.http://www.acfun.love/category.php
3.https://www.luogu.com.cn/problem/list?type=luogu&page=1&difficulty=1|3&tag=160
点点赞赏,手留余香
共 0 人
已开启创作声明
作者已开启创作声明,代表内容为独立创作
允许规范转载
可对作品内容进行复制和转载,但需注明作品作者、出处
禁止转载或摘编
不得对作品内容进行复制和转载
![[图论 – 最短路][Floyd][dijkstra][spfa][Bellman]最短路-零修论坛](https://www.lxwow.top/wp-content/uploads/2026/07/932-2.png)




- 最新
- 最热
查看全部NO.000008