看到还没人用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 array_diff() 函数

    实例 比较两个数组的值,并返回差集: <?php $a1=array("a"=>"red","b"=>"gree ...

  2. PHP timezone_location_get() 函数

    ------------恢复内容开始------------ 实例 返回指定时区的位置信息: <?php$tz=timezone_open("Asia/Taipei");ec ...

  3. PHP zip_open() 函数

    定义和用法 zip_open() 函数打开 zip 档案以供读取.高佣联盟 www.cgewang.com 如果成功,该函数则返回 zip 文件资源.如果失败,则返回 FALSE. 语法 zip_op ...

  4. PDO::rollBack

    PDO::rollBack — 回滚一个事务(PHP 5 >= 5.1.0, PECL pdo >= 0.1.0) 说明 语法 bool PDO::rollBack ( void )高佣联 ...

  5. BSOJ 5445 -- 【2018雅礼】树 prufer序列 dp

    BSOJ在哪我也不知道 没有链接. 对于有标号无根树的统计和有度数限制 一般采用prufer序列. 根据prufer序列 容易知道 某个点的出现次数+1为当前点的度数. 对于这道题 考虑设f[i][j ...

  6. C 语言学习 --2

    memset Declaration: void *memset(void *str, int c, size_t n); Copies the character c (an unsigned ch ...

  7. AI测温落地趋势:已成日常刚需 产品形态呈细分化发展

    现如今不管走到哪儿,机场.车站.医院.商场.超市等公共场所都已经将体温检测作为常态化防疫手段.自全球疫情发生以来,不管欧洲.亚洲,还是中东.东南亚等国家都已经意识到,疫情防控的第一道关口便是测温. 而 ...

  8. 使用QT实现一个简单的登陆对话框(纯代码实现C++)

    使用QT实现一个简单的登陆对话框(纯代码实现C++) 效果展示 使用的QT控件 控件 描述 QLable 标签 QLineEdit 行文本框 QPushButton 按扭 QHBoxLayout 水平 ...

  9. java 异常二

    一 捕获异常try…catch…finally 捕获:Java中对异常有针对性的语句进行捕获,可以对出现的异常进行指定方式的处理 捕获异常格式: try { //需要被检测的语句. } catch(异 ...

  10. java 异常一

    一 异常的继承体系 在Java中使用Exception类来描述异常. 查看API中Exception的描述,Exception 类及其子类是 Throwable 的一种形式,它用来表示java程序中 ...