[图论 – 最短路][Floyd][dijkstra][spfa][Bellman]最短路

[图论 – 最短路][Floyd][dijkstra][spfa][Bellman]最短路


Warning: Undefined variable $page_id in /www/wwwroot/www.lxwow.top/wp-content/themes/zibll1/functions.php on line 78

[图论 – 最短路][Floyd][dijkstra][spfa][Bellman]最短路

最短路介绍 一、核心定义 图论最短路问题,是在带权图中寻找两个节点之间路径总权值最小的路径,权值可代表距离、时 […]

AI摘要
AI摘要 此内容由AI根据正文内容自动生成
AI 正在生成摘要...

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‌.标记已确定状态 将节点uvisited值设为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 相关。对于稀疏图(EV),复杂度约为O(VlogV),远优于O(V^2)。

3‌.惰性删除‌:堆不支持直接修改内部元素的值。因此,当发现更短路径时,我们不是修改堆中旧元素,而是直接插入一个新元素。旧元素会在后续被弹出时通过 if (d > dist[u]) continue; 被忽略。

选朴素版

  • 节点数 V≤1000 左右。

  • 图非常稠密(几乎每两个点之间都有边)。

  • 代码实现简单,不易出错,适合面试手写或小型项目。

选堆优化版

  • 节点数 V>1000,甚至达到 10^5 或 10^6。

  • 图比较稀疏(如地图导航、社交网络关系)。

  • 对性能要求高,是工程实践和算法竞赛的主流选择。

深入 – Bellman-Ford 算法

Bellman-Ford 是解决单源最短路径问题的基础算法,其核心优势在于能处理‌负权边‌并检测‌负权环‌,且天然支持‌限制路径边数/节点数‌的场景。

1. 核心原理

基于动态规划思想,通过反复对图中所有边进行“松弛”操作,逐步逼近最短路径。

  • 松弛操作‌:对于每条边 uv,权重为 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(VE),其中 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(kE),其中 k 为节点平均入队次数。在随机稀疏图中,k 通常很小(约 2-3),效率接近 O(E)。

  • 最坏时间复杂度‌:O(VE),与 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 数组 + 队列
关键逻辑 每轮迭代前复制 distbackup,松弛时读 backupdist 更新距离时同步更新 cnt,若 cnt >= n 则判定有负环
适用场景 航班中转限制、分层网络路由、有步数限制的游戏地图 金融套利检测、判断系统稳定性、含负权图的可行性分析
时间复杂度 O(km) 平均 O(m),最坏 O(nm)‌

深入 – 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. 状态定义

设 distidis**ti 为节点 ii 到节点 jj 的最短路径长度。

2. 状态转移方程

dist[i][j]=min(disti, dist[i][k]+dis*tk) 其中 k 是枚举的“中转点”。算法通过逐步增加允许使用的中转点集合 {1,2,…,k},最终得到允许使用所有节点作为中转点时的全局最短路径。

3. 执行逻辑

算法包含三层嵌套循环:

最外层‌:枚举中转点k(从 1 到 V)。

中间层‌:枚举起点 i(从 1 到 V)。

最内层‌:枚举终点 j(从 1 到 V)。

‌注意:循环顺序必须是K->I->J‌。这是因为在计算dist[i][j] 时,我们需要确保 dist[i][k] 和distk 已经是基于前k−1个中转点计算出的最优解。

二、Floyd vs. 多次调用 Dijkstra

当需要求所有节点对的最短路径时,我们可以选择运行 V 次 Dijkstra,或者运行1次 Floyd。两者的对比如下:

维度 Floyd-Warshall 算法 V 次 Dijkstra (堆优化)
时间复杂度 O(V3) O(VElogV)(稀疏图约 O(V2log⁡V))(稠密图约O(V3logV))
空间复杂度 O(V2) (需存储邻接矩阵) O(V+E) (邻接表)
负权边支持 支持‌ (只要无负权环) 不支持‌ (贪心策略失效)
代码复杂度 极低‌ (核心仅 5 行代码) 较高 (需实现优先队列、松弛逻辑)
适用场景 稠密图‌、小规模图、需处理负权、多源查询频繁 稀疏图‌、大规模图、单源查询为主

关键结论:

  1. 稠密图优势‌:当图非常稠密(边数EV2)时,V 次 Dijkstra 的复杂度约为O(V3logV),而 Floyd 仅为O(V3)。此时 Floyd 不仅代码更简洁,常数因子更小,实际运行往往更快。

  2. 负权边唯一解‌:如果图中存在负权边但没有负权环,Dijkstra 无法使用,而 Floyd 可以直接处理。

  3. 内存瓶颈‌: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],表示从 ij 的最短路径上,i 的下一个节点。

  • 初始化‌:若 ij 有直接边,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‌(针对单源)。

你学会了吗?

尝试一下

图论 – 最短路练习

5043 加工零件

5083 旅游巴士

7691 怪奇的电梯

2653 全源最短路

P1576 最小花费

来源(我就不具体写了,复制粘贴查一下即可):

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

已开启创作声明,禁止转载或摘编
© 版权声明
THE END
喜欢就支持一下吧
点赞1572 分享
评论 共3条

请登录后发表评论