模板-Bellman-Ford&SPFA
Bellman-Ford算法
求最短路的
这个算法基于一个叫做“松弛”的操作
松弛会试图往最短路中加边来缩短最短路
对于这样一个图
1到3的最短路显然是1→2→3而不是1→3绕远路就是最短的捷径
我们所进行的松弛操作就是这样的
松弛时枚举每一条边,并判断先走最短路到达这条边的u点,再经过这条边到达v点,能否使到v点的最短路缩小
若可以,就将到v点的最短路更新
显然,只用一次松弛不能够得到完整的最短路,所以要进行多次松弛
经过一大堆乱七八糟的玄学证明,我们知道只需要进行n-1次松弛即可
所以,当n=1时,我们只要进行0次松弛即可
n=2时,1次即可
n=3,3次即可
n=2147483647,2147483646即可
不行这个数太大了
实际上我们发现,n-1是最大次数
实际上有效松弛有可能没有这么多
所以我们判断如果这次松弛没有让最短路发生变化,就直接结束算法
对于负权环,由于正常情况下最多进行n-1次有效松弛,如果在n-1次全部完成后还可以松弛,就可以说明这里面有负权环了
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | #include <bits/stdc++.h> using namespace std; int n,m; struct Edge { int u,v,w,nxt; }e[10]; int h[10],cnt; int dis[10]; int bak[10]; void add( int u, int v, int w) { e[++cnt].u=u; e[cnt].v=v; e[cnt].w=w; e[cnt].nxt=h[u]; h[u]=cnt; } int main() { cin >> n >> m; for ( int i=1;i<=m;i++) { int a,b,c; cin >> a >> b >> c; add(a,b,c); } for ( int i=1;i<=n;i++) dis[i]=0x3f; dis[1]=0; for ( int k=1;k<=n-1;k++) { for ( int i=1;i<=n;i++) bak[i]=dis[i]; for ( int i=1;i<=m;i++) if (dis[e[i].v]>dis[e[i].u]+e[i].w) dis[e[i].v]=dis[e[i].u]+e[i].w; int check=0; for ( int i=1;i<=n;i++) if (bak[i]!=dis[i]) { check=1; break ; } if (!check) break ; } int flag=0; for ( int i=1;i<=m;i++) if (dis[e[i].v]>dis[e[i].u]+e[i].w) flag=1; if (flag==1) cout << "无最短路" << endl; else { for ( int i=1;i<=n;i++) cout << dis[i] << ' ' ; } return 0; } |
但是我们回头看看这个代码
松弛时枚举每一条边
每一条
根据我的经验,我怀疑这里面有可能有许多没用的松弛
实际上我们可以通过一大堆乱七八糟的玄学证明,松弛只有可能令一部分点的最短路缩小
这一部分点就是松弛时所加入的那条边的v点所连着的点
于是我们就接触到了另一个算法,另一个在Bellman-Ford算法基础上的最短路算法——
Shortest Path Faster Algorithm
它利用队列,每次取出队首,枚举队首所有的边进行松弛
并将那些松弛成功的边的v点入队
以达到避免无效松弛的效果
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | #include <bits/stdc++.h> using namespace std; int n,m; struct Edge { int u,v,w,nxt=-1; }e[10]; int h[10],cnt; int dis[10]; int bak[10]; void add( int u, int v, int w) { e[++cnt].u=u; e[cnt].v=v; e[cnt].w=w; e[cnt].nxt=h[u]; h[u]=cnt; } queue< int > q; int bk[10]; int main() { cin >> n >> m; for ( int i=1;i<=m;i++) { int a,b,c; cin >> a >> b >> c; add(a,b,c); } for ( int i=1;i<=n;i++) dis[i]=999999; dis[1]=0; for ( int i=1;i<=n;i++) bk[i]=0; q.push(1); bk[1]=1; while (!q.empty()) { int k=h[q.front()]; while (k!=-1) { if (dis[e[k].v]>dis[e[k].u]+e[k].w) { dis[e[k].v]=dis[e[k].u]+e[k].w; if (bk[e[k].v]==0) { q.push(e[k].v); bk[e[k].v]=1; } } k=e[k].nxt; } bk[q.front()]=0; q.pop(); } for ( int i=1;i<=n;i++) cout << dis[i] << ' ' ; return 0; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架