Bellman-Ford

Bellman-Ford算法,是单源最短路算法的一种。

与 Dijkstra算法 最大的不同是:Dijkstra算法无法判断含负权边的图的最短路,而Bellman-Ford算法可以处理 存在负权边 的最短路径。

由于Bellman-Ford算法简单地对所有边进行松弛操作,共|V|-1次。所以这个算法的时间效率较低,也正是它的不足之处。

Bellman-Ford的时间复杂度: O(V*E) (V,E分别是点数 与 边数)

图解样例(来源《算法导论》):

伪代码:

INIT(G,s);
for i=1 to |G.V|-1for each edge(u,v)∈G.ERELAX(u,v,w)
for each edge(u,v)∈G.Eif v.d>u.d+w(u,v)return FLASEreturn TRUE

代码如下(邻接矩阵):

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define MAXN 1010
int n,m,ori;
//点,边,起点
struct Edge{ int from,to,cost; }edge[MAXN];
//邻接矩阵
int dis[MAXN];
bool Bellman_Ford()
{  for(int i=1;i<=n;++i) dis[i]=(i==ori ? 0 : INF);  //初始化for(int i=1;i<=n-1;++i)      //n-1for(int j=1;j<=m;++j){int u=edge[j].from,v=edge[j].to;if(dis[v]>dis[u]+edge[j].cost)dis[v]=dis[u]+edge[j].cost;}bool flag=1; //判断是否含有负环//原理:负权环可以无限制的降低总权值,所以如果发现第 n次操作仍可降低总权值,就一定存在负权环。for(int i=1;i<=m;++i){int u=edge[i].from,v=edge[i].to;if(dis[v]>dis[u] + edge[i].cost){flag = 0;break;}  }return flag;
}
int main()
{std::scanf("%d%d%d",&n,&m,&ori); for(int i=1;i<=m;++i)std::scanf("%d%d%d",&edge[i].from,&edge[i].to,&edge[i].cost);  if(Bellman_Ford()) //先判断是否有负环  //这里也可以再加一句判断是否连通,依题而定std::printf("%d", dis[n]);else  std::printf("have negative circle\n");  return 0;
}

SPFA(Shortest Path Faster Algorithm):

Dijkstra有队列优化,Bellman-Ford也有。SPFA是Bellman-Ford的队列优化,减少了不必要的冗余计算。

算法流程:用一个队列来进行维护。初始时将源加入队列。每次从队列中取出一个元素,并对所有与他相邻的点进行松弛,若某个相邻的点松弛成功,则将其入队。直到队列为空时算法结束。

代码如下(邻接表):

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define MAXN 1010
int n,m,ori;//点,边,起点
struct EDGE{int to,val,nxt;}e[MAXN];
//邻接表
int adj[MAXN],dis[MAXN],cnt=0,num[MAXN];//计数器
bool vis[MAXN]={0};
//判断是否在队列
std::queue < int > q;
void addedge(int u,int v,int w) //链式前向星
{e[++cnt].val=w; e[cnt].to=v; e[cnt].nxt=adj[u]; adj[u]=cnt;
}
bool SPFA(int ori)
{for(int i=1;i<=n;++i) dis[i]=(i==ori ? 0 : INF);q.push(ori); vis[ori]=1; ++num[ori];//起点入队列时记得+1 while(!q.empty()){int u=q.front(); q.pop(); vis[u]=0;for(int i=adj[u];i;i=e[i].nxt) {int v=e[i].to;if(dis[v]>dis[u]+e[i].val) {dis[v]=dis[u]+e[i].val;if(!vis[v]){vis[v]=1;q.push(v);++num[v];//记录加入次数 if(num[v]>n)    return 0;//如果这个点加入超过n次,说明存在负环,直接返回 }}}}return 1;
}
int main()
{std::scanf("%d%d%d",&n,&m,&ori); for(int i=1;i<=m;++i){int u,v,w; std::scanf("%d%d%d",&u,&v,&w);addedge(u,v,w); }if(SPFA(ori)); std::printf("%d", dis[n]); return 0;
}

把负环的判断放在外面会更快一点。(但依然不能避免被卡负环)

(所以并没有什么用) 当一个习惯写吧。

bool SPFA()
{for(int i=1;i<=n;++i)dis[i]=INF;q.push(0); dis[0]=0; vis[0]=1; ++num[0];while(!q.empty()){int u=q.front(); q.pop(); vis[u]=0;if(num[u]>=n) return 0;++num[u];//fasterfor(int i=adj[u];i;i=e[i].nxt){int v=e[i].to;if(dis[v]>dis[u]+e[i].val){dis[v]=dis[u]+e[i].val;if(!vis[v]){vis[v]=1; q.push(v);}}}}return 1;
}

代码风格其实和Dijkstra算法很像(代码思想类似BFS)。唯一的区别就是SPFA中需要有计算器来判断是否存在负环。

对于随机数据而言,时间复杂度:O(kE)(k为一个较小系数)

但SPFA可以人为的造数据,卡负环,导致其性能变得非常低。(时间复杂度达到指数级)

所以,如果题目中不存在负权边,用Dijkstra算法最为保险。

SPFA_DFS

SPFA_DFS,用DFS来优化SPFA。就不怕卡负环啦

算法思路:当DFS的过程中第二次搜到某一节点。

就说明这个图中存在一个环。

优缺点:代码简单效率高;但因为递归,空间会大。

代码如下:

void SPFA(int u)
{if(flag) return;vis[u]=1;for(int i=adj[u];i;i=e[i].nxt){int v=e[i].to;if(dis[v]>dis[u]+e[i].val){dis[v]=dis[u]+e[i].val;if(vis[v]){flag=1;break;}else SPFA(v);}}vis[u]=0;
}

做题感悟:

  • 可以运用在最长路中。(将一开始所有边的权值都改为负数,这样就所求出来的就是最长路)
  • SPFA,Bellman-Ford都可以用来判断图中是否存在负环,属于判断负环的模板。
  • 可以用来实现 差分约束算法

Bellman-Ford与SPFA相关推荐

  1. SPFA or bellman ford松弛法--单源最短路

    问题概述:有编号1-n的n个站点,有m条公交车路线,公交车只从一个起点站直接到达终点站,是单向的且每条路线有它自己的车费,有P个人早上从1出发,他们要到达每一个公交站点,然后到了晚上再返回点1,求所有 ...

  2. bellman ford 算法 判断是否存在负环

    Flyer 目录视图 摘要视图 订阅 微信小程序实战项目--点餐系统        程序员11月书讯,评论得书啦        Get IT技能知识库,50个领域一键直达 关闭 bellman for ...

  3. Bellman——Ford算法

    Bellman--Ford 算法介绍 思路讲解 案例分析与代码实现 案例分析 代码实现 优先队列优化(SPFA) 优化原理 代码实现 算法介绍 我们知道Dijkstra算法只能用来解决正权图的单源最短 ...

  4. Bellman Ford算法详解

    一.用途 1. Bellman Ford算法是解决拥有负权边最短路问题的方法之一.还有一种方法是SPFA算法. 2. 二者相比,SPFA算法在效率方面是优于Bellman Ford算法的.但在某些情况 ...

  5. LeetCode 787. K 站中转内最便宜的航班(图/Bellman Ford算法)

    文章目录 贝尔曼-福特算法(Bellman-Ford) 简介 算法思想 算法执行过程 应用 题目描述 分析 代码 LeetCode 787. K 站中转内最便宜的航班 题目描述 Bellman For ...

  6. C++实现bellman ford贝尔曼-福特算法(最短路径)(附完整源码)

    C++实现bellman ford贝尔曼-福特算法 实现bellman ford贝尔曼-福特算法的完整源码(定义,实现,main函数测试) 实现bellman ford贝尔曼-福特算法的完整源码(定义 ...

  7. bellman - ford算法c++

    (最短路III)bellman - ford算法(适用于含负权边的图) 注意:用该算法求最短路,在有边数限制的情况下可以存在负权回路!且对所走的边的数量有要求时只能用该算法实现! 解析:因为如果没有边 ...

  8. 图解Bellman Ford算法

    Bellman Ford 单源最短路径算法[中字] Bellman Ford 单源最短路径算法[中字]_哔哩哔哩_bilibili 贝尔曼-福特算法(Bellman–Ford algorithm )油 ...

  9. Bellman ford算法(贝尔曼·福特算法)

    Bellman - ford算法是求含负权图的单源最短路径的一种算法,效率较低,代码难度较小.其原理为连续进行松弛,在每次松弛时把每条边都更新一下,若在n-1次松弛后还能更新,则说明图中有负环,因此无 ...

  10. Bellman算法和SPFA算法

    Dijkstra算法比较快速,但是如果遇到负边就无能为力了,而Bellman算法可以解决负边问题,只要不是负环. 这个算法数据结构没有讲过,他主要是每次对所以边进行松弛操作,进行n-1次得到最短路径. ...

最新文章

  1. 【HTML】框架集(Framesets)
  2. RIP(Routing Information Protocol)精析04
  3. cpuset(7) — Linux manual page
  4. 【转】简明 Python 教程
  5. 蓝绿色——三色配色篇
  6. python爬股票历史价格_【Python】利用ricequant获取上证指数以及所有股票历史价格数据...
  7. Centos7.5安装redis
  8. 【这是一个还没有被证实是否有效的小白修炼手册】数据结构入门第一课基本概念
  9. 如何设置对话框的最大(小)高度与宽度
  10. 实用的电脑绘图软件——亿图图示
  11. cartographer自动更新地图,2条路径数据合并为1条数据
  12. [everydayNote] 零零散散不成篇
  13. 拿到大厂前端offer的前端开发是怎么回答面试题的
  14. 2020-11-27 PMP 群内练习题 - 光环
  15. Dynamics CRM 无法导出数据
  16. iptables 限速网络
  17. 秒数转化为时间---王子様
  18. [HillStone] 山石防火墙机制
  19. 【reverse】buu-CrackRTF——提取PE中的resource、rtf的固定文件头
  20. win7老计算机,SuperFetch对Win7老电脑的影响

热门文章

  1. 127.0.0.1、192.168.0.111、本机地址、URL
  2. Python标准库:内置函数complex([real[, imag]])
  3. 计算机课堂热身游戏,简单的两款小游戏,带你回味和同学在电脑课上的时光
  4. linux 终端打开chrome,我应该输入什么命令来从终端运行Chrome?
  5. 面阵激光雷达产品调研——Livox Avia
  6. SBUS转485增程方案,SBUS控制远程机器人方案
  7. 治理企业“数据悬河”,阿里云DataWorks全链路数据治理新品发布
  8. UG NX 12 草图设计
  9. 流行和声(7)min7b5/半减七和弦
  10. 【阿里天猫精灵专题】TB-02 RGB冷暖灯控制