看到还没人用Bellman-Ford过,赶紧水一发

lz非常弱,求各位大佬轻喷qwq

洛谷题目传送门:P3371

0.“松弛”操作

如果存在一条边\((u,v)\)通过中继的方式可以让起点到\(v\)的距离缩短,那么就通过中继点缩短这个距离。

举个栗子:

(用数组\(dis[]\)来表示起点到每个点的距离,以下同样)

一开始,\(dis[2]=1000\),\(dis[3]=2\)(默认起点为1,以下同样)

通过3中继明显比从1直接到2要短,于是我们把\(dis[2]\)更新为\(dis[3]+3=5\)(从起点到3再到5)

于是你理解了什么是松弛

1.Bellman-Ford

Bellman-Ford的思想是用每条边进行松弛,每条边松弛\(n-1\)次,就一定能求出起点到每个点的距离

(如果你能感性理解你就不用看下面了)

为什么?因为每松弛1轮(我们管用每条边都松弛一次叫一轮),最短路就至少“生长”1个点。

再举个例子:

这是个很美丽的有向图。

最开始,除了\(dis[1]\)之外,其他所有\(dis\)都是无穷大。(到不了距离不就是无穷大嘛)。

第1轮松弛:

\(dis[1]=0\)(自己到自己距离肯定是0)

\(dis[2]=min(dis[2],dis[1]+1)=min(INF,1)=1\)

\(dis[3]=min(dis[3],dis[1]+5)=min(INF,1)=1\)

(由于边的顺序问题,松弛出来的结果可能不太一样)

(最坏情况可能只松弛1层)

(现在你明白“生长”是什么意思了吗)

第2轮松弛:

\(dis[4]=min(dis[4],dis[2]+2)=min(INF,3)=3\)

第3轮松弛:

\(dis[3]=min(dis[3],dis[4]+1)=min(5,4)=4\)

正好松弛了\(n-1\)轮。

因为,一个图最多有\(n-1\)层:例如,\(1->2->3->...->n\),这个图(最坏情况)就正好要松弛\(n-1\)轮。

于是你理解了Bellman-Ford。

Code:

#include <bits/stdc++.h>
using namespace std;
#define MAXN 500005
#define INF 0x7fffffff
struct edge{
int u,v,w;
edge(){u=v=w=0;}
};
edge g[MAXN],cnt;
int n,m,s,dis[MAXN];
int main(){
scanf("%d%d%d",&n,&m,&s);
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
g[i].u=u;
g[i].v=v;
g[i].w=w;//存边
}
fill(dis+1,dis+1+n,INF);
dis[s]=0;
for(int i=1;i<=n-1;i++){//松弛n-1轮
for(int j=1;j<=m;j++){
int u=g[j].u,v=g[j].v,w=g[j].w;
if(dis[u]==INF){continue;}//如果当前边的起点到不了那就没法松弛
if(dis[v]>dis[u]+w){
dis[v]=dis[u]+w;
}
}
}
for(int i=1;i<=n;i++){
printf("%d ",dis[i]);
}
printf("\n");
return 0;
}

一交,70pts,所以还要优化。

3.优化

但是,很多情况根本不用松弛\(n-1\)轮。

如果松弛到中间一轮松弛不动了(也就是\(dis\)不变了),那么以后再怎么松弛也不会变(因为已经求出最优解了),为什么可以思考一下。(其实就是我懒得写了233)

AC Code:

#include <bits/stdc++.h>
using namespace std;
#define MAXN 500005
#define INF 0x7fffffff
struct edge{
int u,v,w;
edge(){u=v=w=0;}
};
edge g[MAXN],cnt;
int n,m,s,dis[MAXN];
int main(){
scanf("%d%d%d",&n,&m,&s);
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
g[i].u=u;
g[i].v=v;
g[i].w=w;
}
fill(dis+1,dis+1+n,INF);
dis[s]=0;
for(int i=1;i<=n-1;i++){
bool flag=true;
for(int j=1;j<=m;j++){
int u=g[j].u,v=g[j].v,w=g[j].w;
if(dis[u]==INF){continue;}
if(dis[v]>dis[u]+w){
dis[v]=dis[u]+w;
flag=false;
}
}
if(flag){
break;//如果没有松弛过那就没必要再松弛了
}
}
for(int i=1;i<=n;i++){
printf("%d ",dis[i]);
}
printf("\n");
return 0;
}

4.时间复杂度

最坏情况\(O(nm)\),最好情况\(O(m)\)(但是只有RP爆表的情况下才会达到),非常离谱。如果不是因为数据水 人品好玄学原因,几乎一定会TLE。

还有一种Bellman-Ford优化,叫SPFA已死。所以,我建议在座的各位dalao先学Bellman-Ford再学SPFA

SPFA的最坏情况\(O(nm)\),最好情况\(O(m)\),但是除非故意卡(非常容易被卡!),一般都是\(O(m)\)

SPFA留着以后再讲吧,LZ要去吃饭了。

Bellman-Ford算法 例题:P3371 单源最短路径的更多相关文章

  1. SPFA板子 (背景:Luogu P3371 单源最短路径)

    Luogu P3371 单源最短路径 题目描述 如题,给出一个有向图,请输出从某一点出发到所有点的最短路径长度. 输入输出格式 输入格式: 第一行包含三个整数N.M.S,分别表示点的个数.有向边的个数 ...

  2. 【算法导论】单源最短路径之Dijkstra算法

    Dijkstra算法解决了有向图上带正权值的单源最短路径问题,其运行时间要比Bellman-Ford算法低,但适用范围比Bellman-Ford算法窄. 迪杰斯特拉提出的按路径长度递增次序来产生源点到 ...

  3. 【算法导论】单源最短路径之Bellman-Ford算法

    单源最短路径指的是从一个顶点到其它顶点的具有最小权值的路径.我们之前提到的广度优先搜索算法就是一种无权图上执行的最短路径算法,即在所有的边都具有单位权值的图的一种算法.单源最短路径算法可以解决图中任意 ...

  4. 经典贪心算法(哈夫曼算法,Dijstra单源最短路径算法,最小费用最大流)

    哈夫曼编码与哈夫曼算法 哈弗曼编码的目的是,如何用更短的bit来编码数据. 通过变长编码压缩编码长度.我们知道普通的编码都是定长的,比如常用的ASCII编码,每个字符都是8个bit.但在很多情况下,数 ...

  5. 洛谷P3371单源最短路径SPFA算法

    SPFA同样是一种基于贪心的算法,看过之前一篇blog的读者应该可以发现,SPFA和堆优化版的Dijkstra如此的相似,没错,但SPFA有一优点是Dijkstra没有的,就是它可以处理负边的情况. ...

  6. 【luogu P3371 单源最短路径】 模板 SPFA

    题目链接:https://www.luogu.org/problemnew/show/P3371 我永远都喜欢Flyod.dijkstra + heap.SPFA #include <cstdi ...

  7. 【luogu P3371 单源最短路径】 模板 dij + heap

    题目链接:https://www.luogu.org/problemnew/show/P3371#sub 堆优化迪杰斯特拉,留着以后复习用 #include <iostream> #inc ...

  8. 洛谷P3371单源最短路径Dijkstra版(链式前向星处理)

    首先讲解一下链式前向星是什么.简单的来说就是用一个数组(用结构体来表示多个量)来存一张图,每一条边的出结点的编号都指向这条边同一出结点的另一个编号(怎么这么的绕) 如下面的程序就是存链式前向星.(不用 ...

  9. luogu p3371 单源最短路径(dijkstral

    本来我写的对的 我就多手写了个 ios::sync_with_stdio(false); 我程序里面用了cin 还有scanf 本来想偷偷懒 我就说 我查了半天错 根本找不到的啊... 后来交了几次 ...

随机推荐

  1. PHP zip_entry_name() 函数

    定义和用法 zip_entry_name() 函数返回 zip 档案的名称.高佣联盟 www.cgewang.com 语法 zip_entry_name(zip_entry) 参数 描述 zip_en ...

  2. luogu P2973 [USACO10HOL]Driving Out the Piggies G 驱逐猪猡

    luogu LINK:驱逐猪猡 bzoj LINK:猪猪快跑 问题是在1时刻有个炸蛋在1号点 这个炸弹有p/q的概率爆炸 如果没有爆炸 那么会有1/di的概率选择一条边跳到另外一个点上重复这个过程. ...

  3. Python实现图片滑动式验证识别

    1 abstract 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手.很多已经做案例的人,却不知道如何去学习更加高深的知识.那么针对这三类 ...

  4. 读源码从简单的集合类之ArrayList源码分析。正确认识ArrayList

    一.查看源码的方法 1.看继承结构 看这个类的层次结构,处于一个什么位置,可以在自己心里有个大概的了解. 我是有idea查看的, eg:第一步: 第二步: 第三步:查看子类或者继承关系:F4 2.看构 ...

  5. JavaScript String.prototype 原型

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head ...

  6. C#LeetCode刷题之#844-比较含退格的字符串​​​​​​​(Backspace String Compare)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/4030 访问. 给定 S 和 T 两个字符串,当它们分别被输入到空 ...

  7. Vue 修饰符sync的应用

    官方链接 https://cn.vuejs.org/v2/guide/components-custom-events.html#sync-修饰符 这个解释有点不太直观,用代码解释一下 父组件 v-b ...

  8. JS实例-全选练习

    <!DOCTYPE html><html lang="zh"><head> <meta charset="UTF-8" ...

  9. 《Java从入门到失业》第二章:Java环境(二):JDK、JRE、JVM

    2.2JDK.JRE.JVM 在JDK的安装目录中,我们发现有一个目录jre(其实如果是下一步下一步安装的,在和JDK安装目录同级目录下,还会有一个jre目录).初学Java的同学,有时候搞不清楚这3 ...

  10. 《Java从入门到失业》第一章:计算机基础知识(二):计算机组成及基本原理

    1.2计算机组成及基本原理 1.2.1硬件组成 这里说的计算机主要指微型计算机,俗称电脑.一般我们见到的有台式机.笔记本等,另外智能手机.平板也算.有了一台计算机,我们就能做很多事情了,比如我在写这篇 ...