Spfa 求最短路
SPFA 求解最短路代码
c++
/* * Spfa 最短路算法:Shortest Path Faster Algorithm * 前言: * 先前,我们介绍了基于点的 Dijkstra 最短路 和 基于边的 Bellman-Ford算法,他们各有各的长处。这次我们介绍一下应用也非常广泛的 spfa 算法。 * spfa 是受到 Bellman-Ford算法的启发所产生的,并和基于点的最短路的算法有些相似。 * 因为 Bellman-Ford 是完全遍历边,大多数边对于 dist 的更新毫无作用,白白消耗了算力。因此,我们能不能将更新的 dist 点加入到队列中, * 而后只是遍历更新的点,用其的边更新其他点。 * 算法: * 首先规定源点src,终点为 tgt。初始化 dist[src] = 0,并将 src 放入队列 que 中,标记 st[src]= true,表示其在队列中。 * 当队列不为空时: * 取出队列头 u,并将 st[u] = false,更新 u->v 的 dist,倘若 v 点被更新,我们将其加入到队列中(前提是他之前不在队列中) * 复杂度: * O(NM)。说实话这个复杂度还是挺难想的。 * Dijkstra最短路 朴素方法o(N^2)可以理解。使用堆优化之后,因为堆的大小最大为 M,复杂度为 O(M log M) ,稍稍有些难。比如说为什么堆大小最大为 M。 * Bellman-ford 算法复杂度 O(NM),这是因为他最多遍历 N - 1 次边的集合。 * 那么 spfa 算法 O(NM) 是怎么得到的。说实话挺难想的,我觉着因为他是从 Bellman-ford算法优化而来的,因此根据 Bellman-ford最对比,更容易理解。 * 中心思想,我们找到距离 src 最先发现的近点,次先发现的近点,...,最后发现的近点的距离。我们不妨用 1st, 2ed, 3rd,...i th, (i + 1) th, n th表示。 * 解释什么是最发现近点。最先发现近点是 dist 最先确定,且以后不会更新的点。他和最近点不同。因为题目中边权可能为负数。 * 举个例子,原点为 1, 图中有边 1->2 权值为3, 1->3 权值为4, 1->4 权值为10, 4->2权值为-10。 * 最近点是 2点,走的路径是 1->4->2,也就是说,然而2点距离1点最近,但是它的路径长度反倒更长。应用 Bellman-ford的思想,他反倒是 k=2时才可以找到。 * 最先发现近点,次先发现近点是按照 Bellman-Ford的顺序来的。这是因为我们需要参照 Bellman-ford 分析其复杂度。 * * 第一发现近点,遇到的概率为 O(M),src 其实向外扩散一圈就可以知道了。我们假设,每次 O(M) 向外扩充的话,最后就是 O(NM)。 * * 或者是按照第二个思路来想,路径 src-> tgt 为 path = src -> node 1 -> node 2 -> node3 -> ... -> node i -> node (i+1) -> ... -> tgt * src -> node 1需要 O(N), node i -> node (i + 1) -> 这是需要更新将 node i 上相临边其他更新的点都更新一遍,也是 O(M) (想想BFS情景即可)。 * 其实这个想法,也是 Bellman-ford的证明思路。每一次遍历所有边,仅仅是 node i -> node (i + 1)。 * * 其实仔细一想 Spfa 算法和 BFS 也比较像,因为他就是向外扩充加队列。不过队列中的数据因边长不确定,可能会来回遍历。 * BFS求解最短路要求边长一致,因为这样可要保证扩充的层次性。 SPFA 就不断增加 queue 来改进了这一点。 * 优点: * spfa 算法的平均速度可以比拟 dijkstra * 算法整个设计过程中无任何假设,负边是可以容忍的。(如果存在负环,就会死循环) * 缺点: * 虽然,最坏复杂度基本达不到,但是万一被卡,真的就 GG 了。 * */ #include <iostream> #include <cstdio> #include <cstring> #include <string> #include <queue> #include <vector> #include <algorithm> using namespace std; const int N = 100010, INF = 0x3f3f3f3f; int h[N], e[N], ne[N], w[N], idx; int n, m; bool st[N]; int dist[N]; void add(int a, int b, int c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++; } int spfa(int src, int tgt) { // initialize queue<int> que; memset(st, false, sizeof st); memset(dist, 0x3f, sizeof dist); dist[src] = 0; st[src] = true; que.push(src); int u, v; while (que.empty() == false) { u = que.front(); que.pop(); st[u] = false; for (int i = h[u]; ~i; i = ne[i]) { v = e[i]; if (dist[v] > dist[u] + w[i]) { dist[v] = dist[u] + w[i]; if (st[v] == false) { st[v] = true; que.push(v); } } } } return dist[tgt]; } int main() { // initialize memset(ne, -1, sizeof ne); memset(h, -1, sizeof h); idx = 0; // input scanf("%d%d", &n, &m); int a, b, c; while (m -- ) { scanf("%d%d%d", &a, &b, &c); add(a, b, c); } int res = spfa(1, n); if (res >= INF / 2) { printf("impossible"); } else { printf("%d\n", res); } return 0; }
python
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)