【概述】

Bellman-Ford算法适用于计算单源最短路径,即:只能计算起点只有一个的情况。

其最大特点是可以处理存在负边权的情况,但无法处理存在负权回路的情况。

其时间复杂度为:O(V*E),其中,V 是顶点数,E 是边数。

【算法分析】

Bellman Ford 算法与 Dijkstra 算法的思想相同,只不过 Dijkstra 是每次确定一个最短距离点,并用这个点去更新与之相连的其他边,而 Ford 算法是每次更新所有的边,从而确定一个点的最短距离

起始时,认为起点是白点(dis[1]=0),每一次都枚举所有的边,必然会有一些边,连接着白点和蓝点。因此每次都能用所有的白点去修改所有的蓝点,每次循环也必然会有至少一个蓝点变成白点。

以下图为例

令起点为白点,即:dis[1]=0,dis[2、3、4、5]=∞

遍历所有边,将与白点相连的蓝点变为白点,即:dis[1]=0,dis[2]=2,dis[3]=1,dis[4]=2,dis[5] =∞

继续向下遍历,修改蓝点为白点,即:dis[1]=0,dis[2]=2,dis[3]=1,dis[4]=2,dis[5] =4

【算法核心】

设起点为 s,dis[v] 表示从 s 到 v 的最短路径,u[i] 和 v[i] 分别表示第 i 条边的起点和终点,w[j] 是连接 u、v 的边 j 的长度。

初始化:

dis[s]=0,dis[v]=0x3f3f3f3f(v≠s),即:初始化为一极大值

算法主体:

void Bellman_Ford()
{for(int i=0;i<n;i++) dis[i]=INF;dis[0]=0;for(int i=1;i<=n-1;i++)//枚举除终点外的所有点for(int j=1;j<=m;j++)//枚举所有边{int x=u[j];//边j的起点int y=v[j];//边j的终点if(dis[x]<INF)//松弛dis[y]=min(dis[y],dis[x]+w[j]);}
}

算法结束:dis[v] 即为 s 到 v 最短距离

【SPFA】

SPFA 实质就是 Ford 算法加了判断负环的队列实现版本。

其利用队列以进行 Ford 算法的过程,初始时将起点加入队列,每次从队列中取出一个元素,并对所有与它相邻的点进行修改,若相邻的点修改成功,则将其入队,直到队列为空时算法结束。

SPFA 时间复杂度可达:O(k*E),其中,E 是边数,k 是常数,平均值是 2。

int dis[N];
bool vis[N];
void SPFA(int S) {memset(vis, false, sizeof(vis));memset(dis, INF, sizeof(dis));dis[S] = 0;queue<int> Q;Q.push(S);while (!Q.empty()) {int x = Q.front();Q.pop();vis[x] = false;for (int i = head[x]; i != -1; i = edge[i].next) {int to = edge[i].to;if (dis[to] > dis[x] + edge[i].dis) {dis[to] = dis[x] + edge[i].dis;if (!vis[to]) {vis[to] = true;Q.push(to);}}}}
}

【SPFA 的 SLF 优化】

在 SPFA 的基础上,可以进一步的进行优化,即 SLF 优化

SLF 优化就是 small label first 优化,由于先扩展最小的点可以尽量使程序尽早结束,因此当加入一个新点 u 的时候,如果此时的 dis[u] 比队首的 dis[q.front()] 小的话,就将点 v 加入队首,否则加入队尾,利用 STL 中的双端队列 deque 即可

struct Edge{int to,dis;
};
vector<Edge> edge[N];
bool vis[N];
int dis[N];
void SPFA(int s) {memset(dis, INF, sizeof(dis));memset(vis, false, sizeof(vis));vis[s] = true;dis[s] = 0;deque<int> Q;Q.push_back(s);while (!Q.empty()) {int x = Q.front();Q.pop_front();vis[x] = 0;for (int i = 0; i < edge[x].size(); i++) {int y = edge[x][i].to;if (dis[y] > dis[x] + edge[x][i].to) {dis[y] = dis[x] + edge[x][i].to;if (!vis[y]) {vis[y] = true;if (!Q.empty() && dis[y] < dis[Q.front()])//加入队首Q.push_front(y);else//加入队尾Q.push_back(y);}}}}
}

【模版】

1.标准版

struct Edge {int to, next;int dis;
} edge[N];
int head[N], tot;
bool vis[N];
int dis[N];
void addEdge(int x, int y, int dis) {edge[++tot].to = y;edge[tot].dis = dis;edge[tot].next = head[x];head[x] = tot;
}
void SPFA(int S) {memset(vis, false, sizeof(vis));memset(dis, INF, sizeof(dis));dis[S] = 0;queue<int> Q;Q.push(S);while (!Q.empty()) {int x = Q.front();Q.pop();vis[x] = false;for (int i = head[x]; i != -1; i = edge[i].next) {int to = edge[i].to;if (dis[to] > dis[x] + edge[i].dis) {dis[to] = dis[x] + edge[i].dis;if (!vis[to]) {vis[to] = true;Q.push(to);}}}}
}
int main(){int n,m;while(scanf("%d%d",&n,&m)!=EOF){memset(head,-1,sizeof(head));for(int i=1;i<=m;i++){int x,y,dis;scanf("%d%d%d",&x,&y,&dis);//无向图添边两次addEdge(x,y,dis);addEdge(y,x,dis);}int S;scanf("%d",&S);SPFA(S);for(int i=1;i<=n;i++)printf("%d\n",dis[i]);}    return 0;
}

2.带负环判断的SPFA

struct Edge {int from, to;int dis;Edge() {}Edge(int from, int to, int dis) : from(from), to(to), dis(dis) {}
};
struct SPFA {int n, m;Edge edges[N]; //所有的边信息int head[N];   //每个节点邻接表的头int next[N];   //每个点的下一条边int pre[N];    //最短路中的上一条弧bool vis[N];int dis[N];int cnt[N]; //进队次数void init(int n) {this->n = n;this->m = 0;memset(head, -1, sizeof(head));}void AddEdge(int from, int to, int dist) {edges[m] = Edge(from, to, dist);next[m] = head[from];head[from] = m++;}bool negativeCycle(int s) { //判负环memset(vis, false, sizeof(vis));memset(cnt, 0, sizeof(cnt));memset(dis, INF, sizeof(dis));dis[s] = 0;queue<int> Q;Q.push(s);while (!Q.empty()) {int x = Q.front();Q.pop();vis[x] = false;for (int i = head[x]; i != -1; i = next[i]) {Edge &e = edges[i];if (dis[e.to] > dis[x] + e.dis) {dis[e.to] = dis[x] + e.dis;pre[e.to] = i;if (!vis[e.to]) {vis[e.to] = true;Q.push(e.to);if (++cnt[e.to] > n)return true;}}}}return false;}
} spfa;
int main() {int n, m;while (scanf("%d%d", &n, &m) != EOF) {spfa.init(n);int S;scanf("%d", &S);for (int i = 1; i <= m; i++) {int x, y, dis;scanf("%d%d%d", &x, &y, &dis);//无向边添边两次spfa.AddEdge(x, y, dis);spfa.AddEdge(y, x, dis);}spfa.negativeCycle(S);for (int i = 1; i <= n; i++)printf("%d\n", spfa.dis[i]);}return 0;
}

图论 —— 最短路 —— Bellman-Ford 算法与 SPFA相关推荐

  1. 图论最短路:Bellman-Ford与其优化SPFA算法的一点理解

    文章目录 前言 一.对Bellman-Ford的深入理解 1. Bellman-Ford有什么用? 2. 什么是松弛操作? 3. Bellman-Ford的k次迭代意义? 4. 一个重要定理 5. 对 ...

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

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

  3. Bellman Ford算法详解

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

  4. Bellman——Ford算法

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

  5. bellman - ford算法c++

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

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

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

  7. 图解Bellman Ford算法

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

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

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

  9. bellman ford 算法

    Dijkstra算法是处理单源最短路径的有效算法,但它局限于边的权值非负的情况,若图中出现权值为负的边,Dijkstra算法就会失效,求出的最短路径就可能是错的.这时候,就需要使用其他的算法来求解最短 ...

  10. 图论最短路及生成树(Prim,Djikstra,Spfa,Bellan-ford,kruskal,topsort)

    图论在算法中具有举足轻重的地位,只有学好图才能游刃有余.本文章将介绍图论中一些基础算法,可以说总结的十分全面,文章结尾也会分析各算法的差异,清晰易懂.并附上代码模板. 图论(最短路.生成树) 一.拓扑 ...

最新文章

  1. k8s 更改pod数量限制(默认每个节点最多110组pod)0/3 nodes are available: 3 Insufficient cpu报错排查
  2. dqn在训练过程中loss越来越大_[动手学强化学习] 2.DQN解决CartPole-v0问题
  3. JAVA--虚函数,抽象函数,抽象类,接口
  4. 2D转换中心点transform-origin(CSS3)
  5. 强大的离线字典《Colordict》+多种词库下载地址
  6. 计算机如何隐藏任务栏的程序,Win7隐藏任务栏
  7. 计算机主机的组成部分,计算机主机的组成是有哪些
  8. css3 如何画太极 和 奥运五环
  9. 微信短视频加速,张小龙的步子不妨更大一些!
  10. 【开源】STC单片机免冷启动自动下载器
  11. 如何使用OBS 进行屏幕录制
  12. XILINX FPGA 7K325T 烧录MCS文件后无法自启动的问题
  13. HTML5中的data-id与id
  14. android vector 圆形,Android中矢量图形的那些事 - SVG or Vector
  15. 安装MathType后,在word打开后有警告提示。
  16. html右键滚轮不起作用,鼠标滚轮上下失灵处理方法 为什么鼠标滚轮不管用
  17. 数学建模的影响因素分析方法
  18. iOS开发入门——项目创建
  19. ss使用ipv6地址
  20. Window10-一键睡眠

热门文章

  1. 值得收藏!数据分析最常用的18个概念,终于有人讲明白了
  2. 12款堪称神器的 Chrome 插件,Max 你的工作效率!
  3. RTX移植到STM32F103
  4. Dubbo 高危漏洞!原来都是反序列化惹得祸
  5. 国产数据库 OceanBase 二次刷榜 TPC-C,7 亿 tpmC!
  6. 史上最全Redis总结,你想知道的都在这里啦
  7. JEECG 前端JS国际化实现,采用i18n新技术方案
  8. 七步从AngularJS菜鸟到专家(4和5):指令和表达式
  9. 使用Maven Archetype生成工程报错的解决
  10. CSS, JavaScript, jQuery实现标签页切换