贝尔曼-福特算法

维基百科
作用:求解单源最短路径的一种算法

伪代码(摘自维基百科):

procedure BellmanFord(list vertices, list edges, vertex source)
   // 该实现读入边和节点的列表,并向两个数组(distance和predecessor)中写入最短路径信息

   // 步骤1:初始化图
   for each vertex v in vertices:
       if v is source then distance[v] := 0
       else distance[v] := infinity
       predecessor[v] := null

   // 步骤2:重复对每一条边进行松弛操作
   for i from 1 to size(vertices)-1:
       for each edge (u, v) with weight w in edges:
           if distance[u] + w < distance[v]:
               distance[v] := distance[u] + w
               predecessor[v] := u

   // 步骤3:检查负权环
   for each edge (u, v) with weight w in edges:
       if distance[u] + w < distance[v]:
           error "图包含了负权环"

对步骤1的分析:
将图初始化包括包括两个部分,一个部分是将初始点的距离初始化为0,另一个部分是将其他点到初始点的距离初始化为无穷。这个操作的作用体现在第二步中。
对步骤2的分析:
重复v-1次(v代表图中点的数目)对所有边的遍历,在遍历的过程中更新初始点到图中其他点的最短距离。这里可以来分析一下步骤1的作用。
这里写图片描述
假设有如上所述的一个图(图片取自此博客
因为我们在步骤1中将处原点以外的所有点到原点的距离都初始化为了无穷,所以在第一次遍历边的时候,只有与原点直接相连的点才会被更新距离,原因是更新规则是:
if distance[u] + w < distance[v]:
distance[v] := distance[u] + w)
所以在第一次遍历边的时候被更新的只有B,C,D。
这里写图片描述
图片取自此博客
接下来进行第二次遍历:
这里写图片描述
图片取自此博客
这样多次更新下来,每个点到原点的距离都会被逐渐被更新为到原点的最小距离。
对步骤3的分析

负权边:权重为负数的边。
负权环:图上一个点到图上另一个点的一个环,环上权重和为负数。

步骤三的负权环检测用一个例子来理解
这里写图片描述
这里有四个点,所以需要对所有边遍历3次。

遍历次数 B C D
第一次 1 2 INF
第二次 1 2 2
第三次 1 0 2

遍历完后我们来执行步骤三,按照步骤三的规则依次对所有直接连接的点对进行检测
| AB | AC | BD | DC | CB |
|—|
| NO | NO | NO | NO | YES |
可以发现,在检测CB点对的时候发现了负权环,这也与我们的实际结果相一致。
想法
为什么要将所有边遍历v-1次呢?我的看法是防止当图的结构是链式结构时,如果不检测v-1次的话,将不能求出原点到所有点的最短距离。
这里写图片描述
c++代码实现参考这篇博客

猜你喜欢

转载自blog.csdn.net/qq_40721694/article/details/82390270