核心思想:松弛操作

对于边(u,v),用dist(u)和(u,v)的和尝试更新dist(v):

dist(v) = min(dist(v) , dist(u)+l(u,v)

注:dist(i)为源点(起点)到i点的距离,l(u,v)为u->v的边权。

Bellman-Ford的基本操作是进行多次迭代,每一轮迭代对图上所有边进行松弛操作,直到再一次迭代中没有点的dist发生变化即可停止迭代。为什么呢?不妨假设已经没有dist发生变化了,再进行一轮迭代的话,很显然,之后的迭代没有产生任何作用,dist数组依旧没有改变,反倒增大了时间复杂度,这不是多此一举么。

图解:

​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​

初始:(S为源点)

初始设置为inf无穷大,表示还没有最短路

S A B C D E
0 inf inf inf inf inf

第一轮迭代:

对S点连出的边(s->e,s->a)

S A B C D E
0 7(0+7) inf inf inf 5(0+5)

对A连出的边(a->c)

S A B C D E
0 7 inf 9(7+2) inf 5

对B连出的边(b->a)

S A B C D E
0 7 inf 9 inf 5

       dist(B)还没有找到最短路,更新其他点的最短路径无意义,故对B点的出边不进行松弛

对C连出的边(c->b)

S A B C D E
0 7 7(9+(-2)) 9 inf 5

对D连出的边(d->c,d->a)

S A B C D E
0 7 7 9 inf 5

      dist(D)还没有找到最短路,更新其他点的最短路径无意义,故对D点的出边不进行松弛

对E连出的边(e->d)

S A B C D E
0 7 7 9 6(5+1) 5

已经对所有的边进行了松弛操作,第一轮迭代结束

第二轮迭代

对S点连出的边(s->e,s->a)

S A B C D E
0 7 7 9 6 5

                  无需更新

对A连出的边(a->c)

S A B C D E
0 7 7 9 6 5

                  无需更新

对B连出的边(b->a)

S A B C D E
0 7 7 9 6 5

                 无需更新

对C连出的边(c->b)

S A B C D E
0 7 7 9 6 5

                无需更新

对D连出的边(d->c,d->a)

S A B C D E
0 2(6+(-4)) 7 5(6+(-1)) 6 5

对E连出的边(e->d)

S A B C D E
0 2 7 5 6 5

已经对所有的边进行了松弛操作,第二轮迭代结束

第三轮迭代

与第一第二轮同理(此处直接给出迭代结束的结果)

S A B C D E
0 2 2 4 6 5

第四轮迭代

无任何更新,迭代结束,更新完成

算法分析:

如果最短路存在,一定存在一个不含环的最短路。(理由:对零环和正环,去掉后路径不会边长;对负环,若最短路径中存在负环,那一定不是最短路,负环可以无限绕下去,路径可以是负无穷)

最短路不含环,那么一条最短路径最多经过n-1个点(不含起点),所以最多需要n-1轮松弛操作。

复杂度分析:

最多进行n-1次迭代,每次迭代枚举遍历所有边,尝试通过边进行松弛操作,故复杂度为

O(N-1)*O(M)即O(NM),(注:N为点数,M为边数)

伪代码

for (int i = 0; i <= n; i++)

dist[i] = inf;//初始化为无穷大

dist[s] = 0;//s为起点,自己到自己的最短路为0

for (int k = 1; k <= n - 1; k++)//迭代n-1轮

{

for (int i = 1; i <= m; i++)//枚举每一条边

{

int x = u[i], y = v[i];

if (dist[x] < inf)

dist[y] = min(dist[y], dist[x] + w[i]);//松弛

}

}

 检查有无负环

将dist数组初始化为0,迭代n-1次后进行第n次迭代,如果第n次迭代有进行松弛操作,则一定存在负环,因为不存在负环最多只能进行n-1次松弛操作

代码实现:

void bellman_ford(int s, int end) // s为起点,end为终点

{

memset(dis, 127, sizeof(dis));

dis[s] = 0; //起点最短路为0

pre[s] = -1;

for (int i = 1; i <= n - 1; i++)

{

bool ok = false;

for (int j = 1; j <= m; j++)

{

int x = edge[j].u, y = edge[j].v, w = edge[j].w;

if (dis[x] < (1 << 30) && dis[x] + w < dis[y])

{

dis[y] = dis[x] + w;

pre[y] = x; // y的上一个点为x,如不需打印路径无需pre数组

ok = true;

}

}

if (ok == false)

{

break; //未进行松弛操作,提前退出循环,减小时间复杂度

}

}

if (dis[end] < (1 << 30))

cout << dis[end] << "\n";

else

cout << "-1\n";

// Print_Path(end); //打印路径

}

模板题 

题目链接:最短路 - 题目 - Daimayuan Online Judge

题目描述:

给你一张简单有向图,边权都为非负整数。以及一些询问,询问两个点之间的距离。

图用以下形式给出:

第一行输入三个整数 n,m,k表示图的顶点数、边数和询问次数,顶点编号从 1 到 n。

接下来 m 行,每行三个整数 x,y,z表示 x 到 y 有一条有向边,边权为 z。

接下来 k 行,每行两个整数 x,y 询问从 x 到 y 的最短路长度,如果无法到达,输出 −1。

输入格式:

第一行三个整数 n,m,k 表示图的顶点数、边数和询问次数。

接下来 m 行,每行有三个整数,代表一条边。

接下来 k 行,每行有两个整数,代表一次询问。

输出格式:

输出共 k 行,每行一个数表示一次询问的答案。

数据规模: 

对于所有数据,保证 2≤n≤5000,0≤m≤10000,1≤k≤5,1≤x,y≤n,x≠y,1≤z≤10000。

样例输入:

3 3 2

1 2 3

2 3 2

3 2 1

1 3

3 1

样例输出:

5

-1

直接给代码了

#include <bits/stdc++.h>
using namespace std;
struct Edge
{
int u, v, w;
} edge[100009];
int pre[100009]; //记录上一个点,为了打印最短路径
int dis[100009], n, m, k; // n为点数,m为边数,dis[i]为起点到i的最短距离
void Print_Path(int x)
{
if (pre[x] == -1)
{
cout << x; //起点的pre为-1,所以x为起点
return;
}
else
{
Print_Path(pre[x]);
cout << "->" << x;
}
}
void bellman_ford(int s, int end) // s为起点,end为终点
{
memset(dis, 127, sizeof(dis));
dis[s] = 0; //起点最短路为0
pre[s] = -1;
for (int i = 1; i <= n - 1; i++)
{
bool ok = false;
for (int j = 1; j <= m; j++)
{
int x = edge[j].u, y = edge[j].v, w = edge[j].w;
if (dis[x] < (1 << 30) && dis[x] + w < dis[y])
{
dis[y] = dis[x] + w;
pre[y] = x; // y的上一个点为x
ok = true;
}
}
if (ok == false)
{
break; //未进行松弛操作,提前退出循环,减小时间复杂度
}
}
if (dis[end] < (1 << 30))
cout << dis[end] << "\n";
else
cout << "-1\n";
// Print_Path(end); //打印路径
}
int main()
{
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr); //关同步流
cin >> n >> m >> k;
for (int i = 1; i <= m; i++) //读入边
{
cin >> edge[i].u >> edge[i].v >> edge[i].w;
}
for (int i = 1; i <= k; i++) // k次询问
{
int x, y;
cin >> x >> y;
bellman_ford(x, y);
}
}

 参考文献:

《算法竞赛,入门经典(第二版)》

2022 Namomo Spring Camp Div2 Day8 直播课

ending

有什么错误之处欢迎指正!不胜感激!

bellman-ford 单源最短路问题 图解的更多相关文章

  1. 单源最短路问题:OJ5——低德地图

    本题就是一道单源最短路问题.由于是稀疏图,我们采用Dijkstra算法. Dijkstra算法原理 Dijkstra算法的步骤 我们把所有的节点分为两个集合:被选中的(visited==1) 和 未被 ...

  2. dijkstra算法解决单源最短路问题

    简介 最近这段时间刚好做了最短路问题的算法报告,因此对dijkstra算法也有了更深的理解,下面和大家分享一下我的学习过程. 前言 呃呃呃,听起来也没那么难,其实,真的没那么难,只要弄清楚思路就很容易 ...

  3. 【模板】Bellman—Fort 单源最短路径算法

    2333 适用于边集储存 #include<bits/stdc++.h> using namespace std; const int inf=0x3fffffff; ],t[],d[], ...

  4. Bellman-Ford算法解决单源最短路问题

    #include<stdio.h> #include<stdlib.h> #include<stdbool.h> #define max 100 #define I ...

  5. hdu 2544 单源最短路问题 dijkstra+堆优化模板

    最短路 Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submis ...

  6. 单源最短路问题--朴素Dijkstra & 堆优化Dijkstra

    许久没有写博客,更新一下~ Dijkstra两种典型写法 1. 朴素Dijkstra     时间复杂度O(N^2)       适用:稠密图(点较少,分布密集) #include <cstdi ...

  7. 单源最短路问题 Dijkstra 算法(朴素+堆)

    选择某一个点开始,每次去找这个点的最短边,然后再从这个开始不断迭代,更新距离. 代码: 朴素(vector存图) #include <iostream> #include <cstd ...

  8. 单源最短路——Bellman-Ford算法

    1.Dijkstra的局限性 Dijkstra算法是处理单源最短路径的有效算法,但它局限于边的权值非负的情况,若图中出现权值为负的边,Dijkstra算法就会失效,求出的最短路径就可能是错的. 列如以 ...

  9. [ACM_图论] Domino Effect (POJ1135 Dijkstra算法 SSSP 单源最短路算法 中等 模板)

    Description Did you know that you can use domino bones for other things besides playing Dominoes? Ta ...

随机推荐

  1. 什么是Spring beans?

    Spring beans 是那些形成Spring应用的主干的java对象.它们被Spring IOC容器初始化,装配,和管理.这些beans通过容器中配置的元数据创建.比如,以XML文件中 的形式定义 ...

  2. notify()和 notifyAll()有什么区别?

    当一个线程进入 wait 之后,就必须等其他线程 notify/notifyall,使用 notifyall,可 以唤醒所有处于 wait 状态的线程,使其重新进入锁的争夺队列中,而 notify 只 ...

  3. java-注解相关

    注解 概念:说明程序的,给计算机看  注释:用文字描述程序 先了解一些怎么正常javadoc文档 1:给类或者方法添加doc注释 2:通过命令javadoc 执行 类.java文件 新建的类: /** ...

  4. 打败算法 —— 删除链表的倒数第n个结点

    本文参考 出自LeetCode上的题库 -- 删除链表的倒数第n个结点,官方的双指针解法没有完全符合"只遍历一遍链表"的要求,本文给出另一种双指针解法 https://leetco ...

  5. mysql实现in子句的limit查询 (转)

    在supesite里面执行一个SQL语句: select * from supe_spaceitems where catid=98 and itemid not in(select itemid f ...

  6. css边距重叠的解决方案

    ** css防止边距重叠的方法 ** 今天整理了一下用css防止边距重叠的几种方法先假设一组dom结构 <div class="parent"> <div cla ...

  7. java中instanceof是怎么用的, 干什么使的,举例!

    instanceof关键字 instanceof是java中固有的关键字, 就像main, public一样,用法:aa instanceof AA 就是问aa是不是AA的一个实例, 是的话,就返回真 ...

  8. SimpleDateForma求日期,2008-11月第6周星期日是几号?

    题目4: 巧妙利用SimpleDateFormat根据各种信息求日期.2008-11月第6周的星期日是几号? import java.text.ParseException;import java.t ...

  9. myEclipse开发内存溢出解决办法myEclipse调整jvm内存大小 java.lang.OutOfMemoryError: PermGen space及其解决方法

    Window->Preferences->MyEclipse->Servers->Tomcat x.x->JDK->Optional Java VM argumen ...

  10. Oracle中between 和 in

    select * from test_s where id between 2 and 12; between 就是左右全闭区间. SELECT columnsFROM tablesWHERE col ...