7、Bellman-Ford与SPFA

 

一、课程目标

  1. 负权图
  2. Bellman-Ford算法
  3. SPFA算法

二、目标详解

1、负权图

1.1 dijkstra对负权的问题

图中含有负权的边,此时用dijkstra算法求不出最短路,因为贪心法则不成立。

例如:假设U集合有{d[mn], d[a], d[b]}:

  • 最小值d[mn]被加入到S集合,并且用mn松弛其邻接边
  • 假设d[x]被松弛,于是U集合变为{d[a], d[b], d[x]}
  • 之后,如果d[x] < d[a]、d[b],则d[x]也加入S集合(s->x的最短路)

这个过程忽略掉了a、b点可能对x的松弛,因为在g[a][x]为正权的前提下,肯定有d[x] < d[a] < d[a] + g[a][x],所以dijkstra算法节约了大量的无效松弛过程。

然而,如果g[a][x]为负数,以上不等式d[x] < d[a] < d[a] + g[a][x]就不一定成立了,那么就需要继续松弛比较,因此dijkstra算法对负权图无效。

1.2 持续松弛原理

对于负权图的某个点x,凡是有边连接到x的点,称为x的前趋点。

显然,需要所有的前趋点都对它松弛一遍,才能得到最短的d[x]。(最小值模式)

假设x的一个前趋点为y,而 y也有前趋点z……。

问题来了,y松弛x的时候,用的是d[y]+w(y, x),但此时z尚未松弛y,因此d[y]可能后面还会变小。于是,后续还要继续用y对x进行松弛。

基于这个思路,出现了两个算法:Bellman-Ford与SPFA。

3、Bellman-Ford算法

从源点s出发,将所有边排序,依次用每个边的起点松弛边的终点,这个过程称为一遍松弛。

算法原理:每个顶点理论上可能被任何前趋顶点松弛,一遍松弛不够(其前趋点可能尚未被松弛),就用多遍松弛,极端情况下最多n-1遍(假设某前趋点的最短距离经过了所有的顶点)。

判定完成

  • 如果某一遍松弛过程,发现每个距离都已经是最短距离,则判定完成。
  • 算法通过一个flag标记初始为false,当有距离被更新为更小时设置为true,如果一遍松弛下来,发现这个flag为false,则判定完成。
  • 实际上,有负环存在则无法计算最短路,因此该flag也可用作判定负环是否存在。

算法设计

  • n-1次循环:每次遍历每条边,用起点对终点进行松弛。
    • 每次通过flag判定是否完成,如完成就提前结束,表示没有负环。
  • 再做一遍松弛,判定是否完成。如果还可以松弛,表示肯定有负环。

伪代码

bool relax() {
  bool flag = true;
  for(所有边)
      if(可以松弛) {
          更新最短距离
           flag = false;
      } 
  reutrn flag;
}

bool bellman_ford(int v0) {
   初始化d[N]和d[v0];
   for(n-1次)
     if(relax(i))
        return true;
    return relax();
}

复杂度

  • Bellman-Ford的时间复杂度为O(VE),效率不高。
  • 优点是支持负权、实现简单,并可以用来找负环。

3、SPFA算法

在Bellman-Ford算法中,每次都用边的起点x对终点y进行松弛,如果该边上一轮已经松弛过,并且之后起点x没有被松弛(d[x]没变小),则有d[x]+w == d[y],所以x是无需松弛的,做了无效运算。

SPFA算法基于这点进行优化,只有被松弛过的点(既d[x]被变小过)才能再执行松弛,从而减少了大量无效的运算。

SPFA算法为Bellman-Ford的一种优化算法,论文时间复杂度为O(kE)(k为小常数,表示顶点平均参与松弛次数),在稠密图上,复杂度接近Bellman-Ford算法的复杂度O(VE)。

SPFA算法一般使用队列来实现(类似BFS的思路,区别在于顶点可重复入队):

void spfa(s) {
  初始化d[N]=oo、vis[N]=false、d[s]=0, vis[s]=true;
  s点入队;
  while(队列不空) {
    出队;
    清除入队标记;
    for(邻接点) {
      if(松弛) {
        更新最短距离;
        if(已入队)  continue;
        入队;
        记录入队状态;
      }
    }
  }
}

判定负环

  • 正常来说,任意一个最短路径经过的点最多n个,既包含n个顶点。
  • 用cnt[i]表示起点到顶点i的最短路径经过的点数,显然,如果cnt[i]>n则有负环
  • 当x松弛y有效(更新)时,设cnt[y] = cnt[v] + 1

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值