首页 > 编程语言 >最短路算法之:SPFA 算法

最短路算法之:SPFA 算法

时间:2024-06-09 23:10:38浏览次数:29  
标签:int 短路 SPFA Bellman Ford 算法 dis

最短路系列:SPFA 算法

大家好,我是Weekoder!

终于,我们的最短路算法 SPFA 闪亮登场了!

虽然话是这么说,但是我还是建议大家先学习 Dijkstra 算法,再来学习 SPFA。

并且我们在这里学习的 SPFA 算法只能通过P3371,并不能通过标准版的P4779

SPFA 的原型——Bellman-Ford

在学习 SPFA 之前,我们要先学习他的原型 Bellman-Ford。其实 Bellman-Ford 都不能说是 SPFA 的原型,SPFA 其实就是 Bellman-Ford,他们是同一个东西。Bellman-Ford 与 Dijkstra 不同,是以边为研究对象的最短路算法。他的思想是这样的:图中有 \(m\) 条边,对于一共 \(n-1\) 次除了起点外其他点的松弛,每一次可以枚举所有 \(m\) 条边。对于当前枚举的边 \(u\to v\),我们可以试图让 \(u\) 松弛 \(v\),也就是松弛这条边。每次枚举 \(m\) 条边,就必定有至少一个点被松弛。与此往复 \(n-1\) 次,就得到了最短路。代码可以说非常短,比 floyd 还好理解。

\(\text{Code:}\)

void bellman_ford() {
	for (int i = 1; i <= n; i++)
		dis[i] = 2147483647;
	dis[s] = 0;
	for (int i = 1; i < n; i++)
		for (int j = 1; j <= m; j++)
			if (dis[e[j].v] > dis[e[j].u] + e[j].w)
				dis[e[j].v] = dis[e[j].u] + e[j].w;
}

还可以注意到,Bellman-Ford 因为是以边为研究对象的,所以是以类似于最小生成树中的存储方式存储边的。

可以看到 Bellman-Ford 的时间复杂度为 \(\Theta(nm)\)。

但是很可惜,这份代码仅能获得 \(70\text{pts}\)。

\(\color{#52C41A}\texttt{7 AC }\color{#052242}\texttt{ 3 TLE}\)

这时候我们就要来优化我们的代码了。

Bellman-Ford 的队列优化——SPFA

是不是感觉绕了一圈又回来了。

确实,Bellman-Ford 的队列优化就是 SPFA。但在国外,人们只会叫他 Bellman-Ford 的队列优化,而只有中国的 OIer 才会喜欢叫 SPFA,因为这个优化并没有改变时间复杂度的上限,最坏的时候仍然会达到 \(\Theta(nm)\)。那么具体是怎么用队列优化呢?

实际上我们注意到,只有被松弛过的点才能继续松弛别的点,那我们就可以利用这一性质进行优化。那我们就可以把松弛过的点放进一个容器里,拿出来的时候再去松弛别的点。拥有这样先进先出特点的容器,没错,就是队列了。这样,就可以减少无意义的枚举,优化时间复杂度。

我们用一个 \(vis\) 数组来标记某个点是否在队列中。

\(\text{Optimal Code:}\)

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

const int N = 1e4 + 5, M = 5e5 + 5;

struct Edge {
	ll to, w;
};

ll n, m, s, dis[N];
bool vis[N];

vector<Edge> nbr[N];

void bellman_ford() {
	queue<int> q;
	for (int i = 1; i <= n; i++)
		dis[i] = 2147483647;
	dis[s] = 0;
	q.push(s);
	vis[s] = 1;
	while (!q.empty()) {
		int cur = q.front(); q.pop();
		vis[cur] = 0;
		for (auto nxt : nbr[cur]) {
			int to = nxt.to, w = nxt.w;
			if (dis[to] > dis[cur] + w) {
				dis[to] = dis[cur] + w;
				if (!vis[to]) {
					q.push(to);
					vis[to] = 1;
				}
			}
		}
	}
}

int main() {
	cin >> n >> m >> s;
	for (int i = 1; i <= m; i++) {
		int u, v, w;
		cin >> u >> v >> w;
		nbr[u].emplace_back((Edge){v, w});
	}
	bellman_ford();
	for (int i = 1; i <= n; i++)
		cout << dis[i] << " ";
	return 0;
}

长的很像 BFS,但是会取消标记。注意 \(vis\) 的意义。

小结

SPFA 最短路算法就这样讲完了。他可以处理负权图,只要图随机,跑的还是很快的。在图中有负权的情况下,必须使用 SPFA 来保证正确性。那么关于 SPFA,就讲到这里。

再见!

标签:int,短路,SPFA,Bellman,Ford,算法,dis
From: https://www.cnblogs.com/Weekoder/p/18240220

相关文章

  • 代码随想录算法训练营第六天
    哈希表常见的三种哈希结构:数组、set(集合)、map(映射)要快速判断一个元素是否出现集合里,考虑哈希法!242.有效的字母异位词题目:给定两个字符串s和t,编写一个函数来判断t是否是s的字母异位词。注意:若s和t中每个字符出现的次数都相同,则称s和t互为字母异位词。解题:......
  • AcWing算法基础课笔记——最小生成树与二分图
    目录朴素版prim算法——模板题AcWing858.Prim算法求最小生成树题目代码Kruskal算法——模板题AcWing859.Kruskal算法求最小生成树题目代码染色法判别二分图——模板题AcWing860.染色法判定二分图题目代码匈牙利算法——模板题AcWing861.二分图的......
  • AcWing算法基础课笔记——求最短路算法
    目录朴素dijkstra算法——模板题AcWing849.Dijkstra求最短路I题目代码堆优化版dijkstra——模板题AcWing850.Dijkstra求最短路II题目代码Bellman-Ford算法——模板题AcWing853.有边数限制的最短路题目代码spfa算法(队列优化的Bellman-Ford算法)——......
  • (算法)判断是否互为字符重排——<哈希表>
    1.题⽬链接:⾯试01.02.判定是否互为字符重排2.题⽬描述:3.解法(哈希表): 算法思路: 1.当两个字符串的⻓度不相等的时候,是不可能构成互相重排的,直接返回false;2.如果两个字符串能够构成互相重排,那么每个字符串中「各个字符」出现的「次数」⼀定是相同的。因此,我们可以......
  • 程序分享--常见算法/编程面试题:最长公共前缀
    关注我,持续分享逻辑思维&管理思维&面试题;可提供大厂面试辅导、及定制化求职/在职/管理/架构辅导;推荐专栏《10天学会使用asp.net编程AI大模型》,目前已完成所有内容,持续上传中。一顿烧烤不到的费用,让人能紧跟时代的浪潮。从普通网站,到公众号、小程序,再到AI大模型网站。干货满满......
  • TypeScript算法每日一题:最富有客户的资产总量(1672)
    作者:前端小王hs阿里云社区博客专家/清华大学出版社签约作者✍/CSDN百万访问博主/B站千粉前端up主题库:力扣题目序号:1672(简单)题目:最富有客户的资产总量给你一个mxn的整数网格accounts,其中accounts[i][j]是第i​​​​​​​​​​​​位客户在第j家银行托管的资产数......
  • 代码随想录算法训练营第十四天|二叉树递归遍历、迭代遍历、统一迭代
    二叉树遍历二叉树主要有两种遍历方式:深度优先遍历:先往深走,遇到叶子节点再往回走。深度优先遍历又分:前序遍历(中、左、右)中序遍历(左、中、右)后序遍历(左、右、中)广度优先遍历:一层一层的去遍历。(后面讲)递归遍历递归三要素确定递归函数的参数和返回值:确定哪些参数是递......
  • 模拟退火(Simulated Annealing, SA)算法是
    模拟退火(SimulatedAnnealing,SA)算法是一种概率型启发式搜索算法,用于寻找优化问题中的全局最优解。它受到冶金学中退火过程的启发,通过模拟金属冷却过程中的退火过程来寻找问题的最优解。以下是使用MATLAB实现模拟退火算法的一个简单示例。这个例子中,我们将使用模拟退火算法来......
  • 单链表相关面试算法题汇总
    技巧汇总快慢指针先找到中间节点如果要调用next..确保当前节点不为空。依次类推。.next不为空是否有环。走过的路。重新走。互相走。画图,分解,暴力法。用hashset插入法翻转。packagemainimport( "fmt" ."github.com/isdamir/gotype")funcAddLNode(h1,h2*L......
  • LeetCode 算法:除自身以外数组的乘积c++
    原题链接......