说完dijkstra算法,有提到过朴素dij算法无法处理负权边的情况,这里就需要用到Bellman-Ford算法,抛弃贪心的想法,牺牲时间的基础上,换取负权有向图的处理正确。


单源最短路径

Bellman-Ford算法

思维

一张有向图,有n个点,m条边,用dis[]数组保存源点到各点的最短距离,可以通过对边进行n-1次的遍历,当其满足dis[v]>dis[u]+w的时候,就对其进行松弛更新,重复n-1次以后就能得到答案,如果n-1次以后还能继续更新,则可以判断图中出现了负权环,思路非常简短。


举例演算

我们依然设置1为源点,为了直观展现算法思路,设定边的输入顺序如下:

2 4 2

3 4 3

1 2 1

1 3 2

次序 dis[1] dis[2] dis[3] dis[4]
初始化 0
1 0 1 2
2 0 1 2 3
3 0 1 2 3

第一次遍历中,由于点2和点4的距离都是无限大,无法松弛,点3和点4同理。点1和点2,点1和点3符合松弛条件,更新。第二次遍历中,点2和点4就可以松弛更新了,点3和点4也是同理。第三次遍历是一次无用遍历,所有边都已经松弛过了。

由此也能够看出,其实不用进行n-1的遍历就可以得到答案了,可以加入一个bool标记来提前结束这个循环过程。


代码实现

时间复杂度O(NM)

#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue> using namespace std; const int MAX = 1000; int u[MAX], v[MAX], w[MAX], dis[MAX];
int n, m; void Ford(int s) {
for (int i = 1; i <= n; i++) dis[i] = 0x7fffffff;
dis[s] = 0;
for (int i = 0; i < n - 1; i++) {
bool check = 0;
for (int j = 0; j < m; j++) {
if ((dis[v[j]] >= 0x7fffffff) && (dis[u[j]] >= 0x7fffffff)) continue;
else {
if (dis[v[j]] > dis[u[j]] + w[j]) {
dis[v[j]] = dis[u[j]] + w[j];
check = 1;
}
}
}
if (!check) break;
}
} int main() {
cin >> n >> m;
for (int i = 0; i < m; i++) cin >> u[i] >> v[i] >> w[i];
int x;
cin >> x;
Ford(x);
cout << endl;
for (int i = 1; i <= n; i++) {
if (dis[i] > 100000) cout << "none" << " ";
else cout << dis[i] << " ";
}
cout << endl;
return 0;
}

SPFA算法

思维

SPFA算法就是用双端队列优化过的Bellman-Ford算法,初始时将源点加入队列。每次选出队首结点,对其的所有出边进行松弛更新,更新成功的点加入队列,同一个结点可能被多次更新,但是同一个结点只能在同时在队列中出现一个,重复这个操作直到队列为空。这里其实有点像是上一篇dij堆优化代码的思路了。只是缺少了贪心。


代码实现

#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
sing namespace std; const int MAX = 1000; int h[MAX * 2], nxt[MAX * 2], to[MAX * 2], co[MAX * 2], dis[MAX], k = 0, book[MAX];
int n, m; void insert(int u, int v, int c) {
nxt[++k] = h[u];
h[u] = k;
to[k] = v;
co[k] = c;
} void SPFA(int s) {
for (int i = 1; i <= n; i++) {
book[i] = 0;
dis[i] = 0x7fffffff;
}
queue<int> que;
que.push(s);
dis[s] = 0;
book[s] = 1;
while (!que.empty()) {
int cur = que.front();
for (int i = h[cur]; i; i = nxt[i]) { if (dis[to[i]] > dis[cur] + co[i]) {
dis[to[i]] = dis[cur] + co[i];
if (book[to[i]] == 0) {
que.push(to[i]);
book[to[i]] = 1;
}
}
}
que.pop();
book[cur] = 0;
}
} int main() {
cin >> n >> m;
int u, v, w;
for (int i = 0; i < m; i++) {
cin >> u >> v >> w;
insert(u, v, w);
}
int x;
cin >> x;
SPFA(x);
for (int i = 1; i <= n; i++) {
if (dis[i] > 100000) cout << "none" << " ";
else cout << dis[i] << " ";
}
cout << endl;
return 0;
}

最短路径——Bellman-Ford算法以及SPFA算法的更多相关文章

  1. 数据结构与算法--最短路径之Bellman算法、SPFA算法

    数据结构与算法--最短路径之Bellman算法.SPFA算法 除了Floyd算法,另外一个使用广泛且可以处理负权边的是Bellman-Ford算法. Bellman-Ford算法 假设某个图有V个顶点 ...

  2. 最短路径:我的理解--SPFA算法

    SPFA算法 求单源最短路的SPFA算法的全称是:Shortest Path Faster Algorithm. 最短路径快速算法-SPFA算法是西南交通大学段凡丁于1994年发表的. 适用范围:给定 ...

  3. Bellman-Ford算法与SPFA算法详解

    PS:如果您只需要Bellman-Ford/SPFA/判负环模板,请到相应的模板部分 上一篇中简单讲解了用于多源最短路的Floyd算法.本篇要介绍的则是用与单源最短路的Bellman-Ford算法和它 ...

  4. Bellman-ford算法、SPFA算法求解最短路模板

    Bellman-ford 算法适用于含有负权边的最短路求解,复杂度是O( VE ),其原理是依次对每条边进行松弛操作,重复这个操作E-1次后则一定得到最短路,如果还能继续松弛,则有负环.这是因为最长的 ...

  5. 最短路径算法 4.SPFA算法(1)

    今天所说的就是常用的解决最短路径问题最后一个算法,这个算法同样是求连通图中单源点到其他结点的最短路径,功能和Bellman-Ford算法大致相同,可以求有负权的边的图,但不能出现负回路.但是SPFA算 ...

  6. 最短路径问题的Dijkstra和SPFA算法总结

    Dijkstra算法: 解决带非负权重图的单元最短路径问题.时间复杂度为O(V*V+E) 算法精髓:维持一组节点集合S,从源节点到该集合中的点的最短路径已被找到,算法重复从剩余的节点集V-S中选择最短 ...

  7. 最短路径算法之四——SPFA算法

    SPAF算法 求单源最短路的SPFA算法的全称是:Shortest Path Faster Algorithm,该算法是西南交通大学段凡丁于1994年发表的. 它可以在O(kE)的时间复杂度内求出源点 ...

  8. 单源最短路径(3):SPFA 算法

    SPFA(Shortest Path Faster Algorithm)算法,是西南交通大学段凡丁于 1994 年发表的,其在 Bellman-ford 算法的基础上加上一个队列优化,减少了冗余的松弛 ...

  9. 【算法】单元最短路径之Bellman-Ford算法和SPFA算法

    SPFA是经过对列优化的bellman-Ford算法,因此,在学习SPFA算法之前,先学习下bellman-Ford算法. bellman-Ford算法是一种通过松弛操作计算最短路的算法. 适用条件 ...

随机推荐

  1. JNI 和 socket api

    1.JavaVM 和 JNIEnvJNIEnv是一个与线程相关的变量,不同线程的JNIEnv彼此独立.JavaVM是虚拟机在JNI层的代表,在一个虚拟机进程中只有一个JavaVM,因此该进程的所有线程 ...

  2. repo配置与连接

    repo是远程访问android源码的工具,和git一起使用. repo的远程安装经常被屏蔽,你懂得. sudo apt-get  install  curl  244  sudo apt-get - ...

  3. Python基础—14-邮件与短信

    邮件与短信 邮件发送 简介: 邮件服务器.账户.密码 相关协议:SMTP.POP3.IMAP 默认TCP协议端口:25 用途:经常用在一个网站的注册激活.通知.找回密码等场景 库:smtplib 示例 ...

  4. 使用终端命令行将本地项目上传到Github并提交代码

    第一步: 在Github上创建自己的repository 第二步:建立本地仓库cd到你的本地项目根目录下,执行git命令 1:$ cd 到你的项目目录下 2:$ git init 第三步:将本地项目工 ...

  5. PTA Java tips(转载)

    在PTA提交Java程序需要注意如下几个要点 1. Main类与Scanner 1.1 Main类 你提交的所有程序都应该以如下形式出现 public class Main{ public stati ...

  6. [HNOI2003]操作系统(优先队列,堆排序)

    题目描述 写一个程序来模拟操作系统的进程调度.假设该系统只有一个CPU,每一个进程的到达时间,执行时间和运行优先级都是已知的.其中运行优先级用自然数表示,数字越大,则优先级越高. 如果一个进程到达的时 ...

  7. 03 shell编程之case语句与函数

    本文所有内容均来自当年博主当年学习笔记,若有不足欢迎指正 Shell编程之case语句与函数 学习目标: 掌握case语句编程 掌握shell函数的使用 目录结构: Case语句 Case语句的作用 ...

  8. php-5.6.26源代码 - hash存储结构 - 初始化

    初始化 有指定析构函数,在销毁hash的时候会调用,如:“类似extension=test.so扩展”也是存放在HashTable中的,“类似extension=test.so扩展”的module_s ...

  9. DSP+ARM多核异构开发环境SYSLINK搭建OMAPL138

    DSP+ARM多核异构开发环境搭建OMAPL138 注意: 环境为Ubuntu 12.04 只能是这个环境.我甚至在Ubuntu16.04上面安装了VMware,然后,在装了一个Ubuntu 12.0 ...

  10. C语言实例解析精粹学习笔记——31

    实例31: 判断字符串是否是回文 思路解析: 引入两个指针变量(head和tail),开始时,两指针分别指向字符串的首末字符,当两指针所指字符相等时,两指针分别向后和向前移动一个字符位置,并继续比较, ...