Bellman-ford 算法详解
昨天说的dijkstra固然很好用,但是却解决不了负权边,想要解决这个问题,就要用到Bellman-ford.
我个人认为Bellman-Ford比dijkstra要好理解一些,还是先上数据(有向图):
7 -6
5 4 -3 -
-3
在讲述开,先设几个数组:
origin[i]表示编号为i这条边的起点编号,如origin[4]=2
destination[i]表示编号为i这条边的终点编号,如origin[5]=5
value[i]表示编号为i这条边的权值,如value[3]=-6
dis[i],和昨天一样,源点到i号点的估计距离,经过不断更新会变成时机距离,就是答案。
bellmanford的实际意义就是扫描一条边,看如果走这条边能不能使这条边的dis[destination[i]],变少,现在我来模拟一下:
初始的dis:[0,∞,∞,∞,∞]
首先从第一条边1 2 8开始,判断走这条边能不能使这条边的终点的dis变短,原本dis[2]=∞,而dis[1]=0,而这条边的权值:value[1]=8,0+8<∞所以将dis[2]更新成8.
dis[0,8,∞,∞,∞]
然后是第二条边,用刚才的方法将dis[3]从∞更新成5.
dis[0,8,5,∞,∞]
第三条2 3 -8,原本的dis[3]=5,如果走第三条边,则dis[3]=dis[2]+value[3]=8+(-6)=2<5,所以dis[3]更新成2.
dis[0,8,2,∞,∞]
以此类推,经过第一轮更新,dis数组如下:
dis[0,8,2,15,0]
但是第一次更新后,并不是最优解于是开始第二次更新。
按照第一次更新的步骤一步一步来得到的答案是
dis[0,8,2,-3,0]
这便是最优解,但是问题来了,一般要更新多少次呢?
n-1次。这样能保证更新出的一定是最优解。
好了,呈上代码:
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
int dis[];
int origin[],destination[],value[];//刚刚说过的三个数组
int n,m;
void Bellman_ford(int a)
{
memset(dis,,sizeof(dis));//赋初始值
dis[a]=;
for(int i=;i<=n-;i++)//更新n-1次
for(int j=;j<=m;j++)//更新每一条边
dis[destination[j]]=min(dis[destination[j]],dis[origin[j]]+value[j]);//判断是否更新
}
int main()
{
cin>>n>>m;
for(int i=;i<=m;i++)
cin>>origin[i]>>destination[i]>>value[i];
Bellman_ford();
for(int i=;i<=n;i++)
cout<<dis[i]<<" ";
}
有些人可能发现了,很多时候实际上不用更新n-1次,因此我们可以用队列优化:
每次选出队首点,对与队首点链接的所有点的dis进行更新,并加入队列,然后队首点pop出队列,
这个算法最好用邻接表实现,代码如下:
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <queue>
using namespace std;
int dis[];
int book[];
int origin[],destination[],value[];
int n,m;
int total;
int next[],head[];
void adl(int a,int b,int c)//邻接表
{
total++;
origin[total]=a;
destination[total]=b;
value[total]=c;
next[total]=head[a];
head[a]=total;
}
void Bellman_ford(int a)
{
memset(book,,sizeof(book));//book[i]表示i号点是否在队列里
memset(dis,,sizeof(dis));
queue <int> q;
q.push(a);
book[a]=;
dis[a]=;
while(!q.empty())//当队列不为空时更新
{
for(int e=head[q.front()];e;e=next[e])//枚举队首点相邻的每一个点
{
if(dis[destination[e]]>dis[origin[e]]+value[e])
{
dis[destination[e]]=dis[origin[e]]+value[e];
if(book[destination[e]]==)
{
q.push(destination[e]);//将更新的这一个点入队
book[destination[e]]=;
}
}
}
q.pop();//弹出队首元素
}
}
int main()
{
cin>>n>>m;
for(int i=;i<=m;i++)
{
int a,b,c;
cin>>a>>b>>c;
adl(a,b,c);
}
Bellman_ford();
for(int i=;i<=n;i++)
cout<<dis[i]<<" ";
}
总结一下,bellman_ford的空间复杂度是m时间复杂度是O(nm),经过队列优化,时间复杂度是<=O(nm)。
Bellman-ford 算法详解的更多相关文章
- BM算法 Boyer-Moore高质量实现代码详解与算法详解
Boyer-Moore高质量实现代码详解与算法详解 鉴于我见到对算法本身分析非常透彻的文章以及实现的非常精巧的文章,所以就转载了,本文的贡献在于将两者结合起来,方便大家了解代码实现! 算法详解转自:h ...
- kmp算法详解
转自:http://blog.csdn.net/ddupd/article/details/19899263 KMP算法详解 KMP算法简介: KMP算法是一种高效的字符串匹配算法,关于字符串匹配最简 ...
- 机器学习经典算法详解及Python实现--基于SMO的SVM分类器
原文:http://blog.csdn.net/suipingsp/article/details/41645779 支持向量机基本上是最好的有监督学习算法,因其英文名为support vector ...
- [转] KMP算法详解
转载自:http://www.matrix67.com/blog/archives/115 KMP算法详解 如果机房马上要关门了,或者你急着要和MM约会,请直接跳到第六个自然段. 我们这里说的K ...
- 【转】AC算法详解
原文转自:http://blog.csdn.net/joylnwang/article/details/6793192 AC算法是Alfred V.Aho(<编译原理>(龙书)的作者),和 ...
- KMP算法详解(转自中学生OI写的。。ORZ!)
KMP算法详解 如果机房马上要关门了,或者你急着要和MM约会,请直接跳到第六个自然段. 我们这里说的KMP不是拿来放电影的(虽然我很喜欢这个软件),而是一种算法.KMP算法是拿来处理字符串匹配的.换句 ...
- EM算法详解
EM算法详解 1 极大似然估计 假设有如图1的X所示的抽取的n个学生某门课程的成绩,又知学生的成绩符合高斯分布f(x|μ,σ2),求学生的成绩最符合哪种高斯分布,即μ和σ2最优值是什么? 图1 学生成 ...
- Tarjan算法详解
Tarjan算法详解 今天偶然发现了这个算法,看了好久,终于明白了一些表层的知识....在这里和大家分享一下... Tarjan算法是一个求解极大强联通子图的算法,相信这些东西大家都在网络上百度过了, ...
- 安全体系(二)——RSA算法详解
本文主要讲述RSA算法使用的基本数学知识.秘钥的计算过程以及加密和解密的过程. 安全体系(零)—— 加解密算法.消息摘要.消息认证技术.数字签名与公钥证书 安全体系(一)—— DES算法详解 1.概述 ...
- 安全体系(三)——SHA1算法详解
本文主要讲述使用SHA1算法计算信息摘要的过程. 安全体系(零)—— 加解密算法.消息摘要.消息认证技术.数字签名与公钥证书 安全体系(一)—— DES算法详解 安全体系(二)——RSA算法详解 为保 ...
随机推荐
- 利用Jsoup模拟跳过登录爬虫获取数据
今天在学习爬虫的时候想着学习一下利用jsoup模拟登录.下面分为有验证码和无验证码的情况进行讨论. ---------------------------无验证码的情况---------------- ...
- Java企业级电商项目架构演进之路 Tomcat集群与Redis分布式
史诗级Java/JavaWeb学习资源免费分享 欢迎关注我的微信公众号:"Java面试通关手册"(坚持原创,分享各种Java学习资源,面试题,优质文章,以及企业级Java实战项目回 ...
- curl错误码77 及 升级libcurl
今天碰到一个问题,curl请求返回错误码77错误 还给出了官网地址,网上查到77对应的是CURLE_SSL_CACERT_BADFILE 想起了刚默认更新了libcurl,于是有手工安装了一下c ...
- MySQL启动很慢的原因
我们在启动MySQL的时候,常常会遇到的是, 当执行启动命令后,它会"Start MySQL ....." 一直不停的执行,也不中断,也不成功 这里会出现此现象的原因有以下三条: ...
- 转载:Logistic回归原理及公式推导
转载自:AriesSurfer 原文见 http://blog.csdn.NET/acdreamers/article/details/27365941 Logistic回归为概率型非线性回归模型,是 ...
- 当想把html element里面的text提取出来可以试着用正则
var a='123<span>456</span><span class="active">789</span>'; a.repl ...
- python基础(8)--迭代器、生成器、装饰器
1.迭代器 迭代器是访问集合元素的一种方式.迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退.另外,迭代器的一大优 ...
- LeetCode818. Race Car
https://leetcode.com/problems/race-car/description/ Your car starts at position 0 and speed +1 on an ...
- Bootstrap – 1.认识
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8&quo ...
- 不修改系统日期和时间格式,解决Delphi报错提示 '****-**-**'is not a valid date and time
假如操作系统的日期格式不是yyyy-MM-dd格式,而是用strtodate('2014-10-01')) 来转换的话,程序会提示爆粗 '****-**-**'is not a valid date ...