昨天说的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 算法详解的更多相关文章

  1. BM算法  Boyer-Moore高质量实现代码详解与算法详解

    Boyer-Moore高质量实现代码详解与算法详解 鉴于我见到对算法本身分析非常透彻的文章以及实现的非常精巧的文章,所以就转载了,本文的贡献在于将两者结合起来,方便大家了解代码实现! 算法详解转自:h ...

  2. kmp算法详解

    转自:http://blog.csdn.net/ddupd/article/details/19899263 KMP算法详解 KMP算法简介: KMP算法是一种高效的字符串匹配算法,关于字符串匹配最简 ...

  3. 机器学习经典算法详解及Python实现--基于SMO的SVM分类器

    原文:http://blog.csdn.net/suipingsp/article/details/41645779 支持向量机基本上是最好的有监督学习算法,因其英文名为support vector  ...

  4. [转] KMP算法详解

    转载自:http://www.matrix67.com/blog/archives/115 KMP算法详解 如果机房马上要关门了,或者你急着要和MM约会,请直接跳到第六个自然段.    我们这里说的K ...

  5. 【转】AC算法详解

    原文转自:http://blog.csdn.net/joylnwang/article/details/6793192 AC算法是Alfred V.Aho(<编译原理>(龙书)的作者),和 ...

  6. KMP算法详解(转自中学生OI写的。。ORZ!)

    KMP算法详解 如果机房马上要关门了,或者你急着要和MM约会,请直接跳到第六个自然段. 我们这里说的KMP不是拿来放电影的(虽然我很喜欢这个软件),而是一种算法.KMP算法是拿来处理字符串匹配的.换句 ...

  7. EM算法详解

    EM算法详解 1 极大似然估计 假设有如图1的X所示的抽取的n个学生某门课程的成绩,又知学生的成绩符合高斯分布f(x|μ,σ2),求学生的成绩最符合哪种高斯分布,即μ和σ2最优值是什么? 图1 学生成 ...

  8. Tarjan算法详解

    Tarjan算法详解 今天偶然发现了这个算法,看了好久,终于明白了一些表层的知识....在这里和大家分享一下... Tarjan算法是一个求解极大强联通子图的算法,相信这些东西大家都在网络上百度过了, ...

  9. 安全体系(二)——RSA算法详解

    本文主要讲述RSA算法使用的基本数学知识.秘钥的计算过程以及加密和解密的过程. 安全体系(零)—— 加解密算法.消息摘要.消息认证技术.数字签名与公钥证书 安全体系(一)—— DES算法详解 1.概述 ...

  10. 安全体系(三)——SHA1算法详解

    本文主要讲述使用SHA1算法计算信息摘要的过程. 安全体系(零)—— 加解密算法.消息摘要.消息认证技术.数字签名与公钥证书 安全体系(一)—— DES算法详解 安全体系(二)——RSA算法详解 为保 ...

随机推荐

  1. Java从零到企业级电商项目实战

    欢迎关注我的微信公众号:"Java面试通关手册"(坚持原创,分享各种Java学习资源,面试题,优质文章,以及企业级Java实战项目回复关键字免费领取)回复关键字:"电商项 ...

  2. ubuntu sudoers改坏了。

    pkexec bash 通过如上命令,可以进入root模式,然后恢复sudoers.前提是root账户没被禁用. precise (1) pkexec.1.gz Provided by: policy ...

  3. Keras自定义评估函数

    1. 比较一般的自定义函数: 需要注意的是,不能像sklearn那样直接定义,因为这里的y_true和y_pred是张量,不是numpy数组.示例如下: from keras import backe ...

  4. springboot + swagger2 生成api文档

    直接贴代码: <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-sw ...

  5. 域名 DNS命令——dig

    dig命令详解   1.查看域名的A记录          # dig yahoo.com; <<>> DiG 9.8.2rc1-RedHat-9.8.2-0.10.rc1.e ...

  6. Linux打补丁的一个简单例子

        前言 在做开发的过程中难免需要给内核及下载的一些源码打补丁或者说是升级,所以我们学习在Linux下使用diff制作补丁以及如何使用patch打补丁显得尤为重要. diff与patch命令介绍 ...

  7. poj 1077(BFS预处理+康托展开)

    Eight Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 29935   Accepted: 13029   Special ...

  8. Hadoop案例(一)之日志清洗

    日志清洗案例 一. 简单解析版 1)需求 去除日志中字段长度小于等于11的日志. 2)输入数据 /Sep/::: +] "-" "Mozilla/4.0 (compati ...

  9. 前端JS框架系列之requireJS基础学习

    1 背景 伴随着项目功能的不断扩充,客户体验的不断完善,实现交互逻辑的JS代码变得越来越多.起初,为了管理JS代码,我们把JS从页面中解放出来独立成文件,接着又把相似的交互代码提取到公共的JS页面中. ...

  10. PHP 中文乱码解决方式

    1.PHP代码在PHP Storm中乱码,设置PHP Storm的默认文件编码格式为UTF-8: 文件->设置->编辑器->文件编码->将所有默认编码调整为UTF-8: 2.对 ...