最短路算法入门(Bellman-Ford算法)

前置:

对于无向图中的一条边(u↔v),可以看作有向图中两条边(u→v)and(v→u)的结合。

我们可以通过这种方式把无向图转化为有向图,

从而使用有向图的最短路算法。

 

图的一些记号:

G = <V,E>代表一个简单有向图(简单图:没有重边和自环)

n = |V|代表顶点数

m = |E|代表边数

l(u,v)代表u到v的边权

S代表起点,T代表终点

dist(u)代表当前求出的S到u的最短路径长度

 

特别地:

对于所有边权都大于等于0的图,

任意两个顶点之间的最短路,

显然不会经过重复的顶点或者边。

也就是说任意一条最短路经过的顶点数不会超过n个,边不会超过n-1条。

而对于有边权为负的图,有可能图中会存在负环,此时途径负环的最短路没有意义。

 

Bellman-Ford算法

核心思想:

松弛操作(Relieve),即对于边(u,v),用dist(u)和l(u,v)的和 尝试更新dist(v):

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

如此,不断尝试对图上的每一条边进行松弛操作。

每次迭代就对所有的边进行该操作,直到某一次迭代没有发生任何改变时停止。

 

在最短路存在的情况下,由于一次迭代会使得最短路的边数至少+1

而S到每个顶点的最短路经过的边数最多为n-1

因此整个算法最多会进行n-1轮迭代,每一轮复杂度为O(m)

则总复杂度为O(nm)

基于此我们得出,当从S出发能到达一个负环时,就会进行n轮以上的迭代

我们可以外加一个超级初节点,使得其到任意一个点的边权为0,

从而在O(nm)的复杂度里检测图上是否存在负环。

代码如下:

复制代码
 1 struct Edge{
 2     int x, y, v;
 3 } edge[M + 1];
 4  5 int n, m, dist[N + 1], pre[N + 1];
 6  7 int shortestpath(int s, int t){
 8     memset(dist, 127, sizeof(dist));        //Initialization
 9     dist[s] = 0;                            //Initialization
10     for (;;){       
11     //在每次迭代中,遍历所有边,尝试用Relieve_Operation更新距离数组
12         bool ok = false;
13         for (int i = 1;i <= m;i++){
14             int x = edge[i].x;
15             int y = edge[i].y;
16             int v = edge[i].v;
17             if (dist[x] + 1 << 30){
18                 if (dist[x] + v < dist[y]){
19                     dist[y] = dist[x] + v;
20                     pre[y] = x;
21                     ok = true;
22                 }
23             }
24         }
25         if (!ok){
26             break;
27         }
28     }
29     return dist[t];
30 }
31 //pre数组是为了打印路径
32 //此代码保证了题目的数据不会出现负环
复制代码

 

大名鼎鼎的SPFA就是Bellman-Ford的队列优化方法,但是不要写就对了,这里也直接略过。

(未完待续......)

posted @   Conqueror712  阅读(100)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示