Bellman-Ford算法
该算法的特性是针对更广泛情况下的最短路径查询,其中边权可以为负数,并且会针对数据判断图中是否有从源节点到达负权环路的路径。
方法依旧基于Class<Graph>实现,其中对少数地方做了对应修正,在带权边最短路算法中需要两个最基本的操作:
-  
初始化操作:使得除了根节点以外的节点
d属性设置为 ∞ \infty ∞,前驱节点设置为空,这里的d属性表示的是该节点距离源节点的上界或标准最小值;void Graph::InitializeSingleSource(string s) { for (auto &i : G) { i._d = Maxnum; i._pi = nullptr; } G[index[s]]._d = 0; } -  
松弛操作:对于一条有向边
(u,v),其边权不变化,但如果出边点距离发生优化,则入边点就需要及时更新:void Graph::Relax(string u, string v) { if (G[index[v]]._d > G[index[u]]._d + weight[{u, v}]) { G[index[v]]._d = G[index[u]]._d + weight[{u, v}]; G[index[v]]._pi = &G[index[u]]; } } 
上述两个基本操作组成了Bellman-Ford算法的基本要素,之后要进行的就是组装。
由于上述提到的其算法可以检测是否存在负权环边,那么函数的就会被定义为布尔类型,此外检测负权环边要根据其特性:无限循环的情况下会被无限优化至负无穷(所以说负无穷的逻辑定义也是数值上定义同步的),最终算法过程被描述为:
bool Graph::BellmanFord(string root)
{
    InitializeSingleSource(root);
    for (int i = 1; i < vertex; i++)
        for (auto it : weight)
            Relax(it.first.first, it.first.second);
    for (auto it : weight)
        if (G[index[it.first.second]]._d > G[index[it.first.first]]._d + it.second)
            return false;
    return true;
}
 
主要分为三个步骤:
- 初始化;
 - ∣ V ∣ − 1 |V|-1 ∣V∣−1次遍历所有边并松弛;
 - 结束松弛后判断是否能继续被松弛,若可以则证明其存在负权环边;
 
并且该算法依旧采用了临界链表储存,而边值为了便于操作选择了map<pair<str,str>,int>实现,这里为了便于实现而并不代表实际性能。

采用图中数据做测验,输出点的d属性为:
s 0
t 2
x 4
y 7
z -2
 
分析
算法时间复杂度
从上述描述可以得出算法主要消耗在
    
     
      
       
        ∣
       
       
        V
       
       
        ∣
       
       
        −
       
       
        1
       
      
      
       |V|-1
      
     
    ∣V∣−1次对所有边的遍历以及常数的松弛操作,所以Bellman-Ford算法的时间复杂度为
    
     
      
       
        O
       
       
        (
       
       
        V
       
       
        E
       
       
        )
       
      
      
       O(VE)
      
     
    O(VE)。
算法正确性
考虑路径
    
     
      
       
        p
       
      
      
       p
      
     
    p为源节点到v的最短路径:
 
     
      
       
        
         p
        
        
         =
        
        
         <
        
        
         
          v
         
         
          0
         
        
        
         ,
        
        
         
          v
         
         
          1
         
        
        
         ,
        
        
         .
        
        
         .
        
        
         .
        
        
         ,
        
        
         
          v
         
         
          k
         
        
        
         >
        
       
       
         p=<v_0,v_1,...,v_k> 
       
      
     p=<v0,v1,...,vk>
 上节所提到的最短路均不包含环路(最短的意义扩展到边的数量上),所以
    
     
      
       
        ∣
       
       
        p
       
       
        ∣
       
       
        ≤
       
       
        ∣
       
       
        V
       
       
        ∣
       
       
        −
       
       
        1
       
      
      
       |p|\le |V|-1
      
     
    ∣p∣≤∣V∣−1,由此在图中优化
    
     
      
       
        ∣
       
       
        V
       
       
        ∣
       
       
        −
       
       
        1
       
      
      
       |V|-1
      
     
    ∣V∣−1次保证了最远的点的所有边都被遍历优化过,结果成立。
假设有一条最短路径存在,那么根据上述引理,最短路径一定存在且被松弛到非无穷的数值范围内;
反过来看如果存在
    
     
      
       
        v
       
       
        .
       
       
        d
       
       
        <
       
       
        ∞
       
      
      
       v.d< \infty
      
     
    v.d<∞,那么在此过程中一定被某个相邻的点进行过松弛优化,所以该点给v的前驱节点,在最短路搜索树中根节点为s,既然在搜索过程中被更新说明根节点是v的前驱的祖先,那么同样也是v的祖先,所以存在一条最短路径。
当图中不包含负权环路时,上述引理证明了算法终止时,源节点到某一节点v可达状态下存在
    
     
      
       
        v
       
       
        .
       
       
        d
       
       
        =
       
       
        δ
       
       
        (
       
       
        s
       
       
        ,
       
       
        v
       
       
        )
       
      
      
       v.d=\delta(s,v)
      
     
    v.d=δ(s,v),不可达其同样满足(值为
    
     
      
       
        ∞
       
      
      
       \infty
      
     
    ∞)。所以在上述要求下,前驱子图确定是一棵最短路径树,同时在最终检验中,因为不存在负权环路所以没有边会在检验中被优化,最终算法返回True。
当图中包含一条负权环路:
 
     
      
       
        
         p
        
        
         =
        
        
         <
        
        
         
          v
         
         
          0
         
        
        
         ,
        
        
         
          v
         
         
          1
         
        
        
         ,
        
        
         .
        
        
         .
        
        
         .
        
        
         ,
        
        
         
          v
         
         
          k
         
        
        
         >
        
       
       
         p=<v_0,v_1,...,v_k> 
       
      
     p=<v0,v1,...,vk>
 同时
    
     
      
       
        
         v
        
        
         0
        
       
       
        =
       
       
        
         v
        
        
         k
        
       
      
      
       v_0=v_k
      
     
    v0=vk,则会得出结论:
 
     
      
       
        
         
          ∑
         
         
          
           i
          
          
           =
          
          
           1
          
         
         
          k
         
        
        
         w
        
        
         
          (
         
         
          
           v
          
          
           
            i
           
           
            −
           
           
            1
           
          
         
         
          ,
         
         
          
           v
          
          
           i
          
         
         
          )
         
        
        
         <
        
        
         0
        
       
       
         \sum_{i=1}^{k} w\left(v_{i-1}, v_{i}\right)<0 
       
      
     i=1∑kw(vi−1,vi)<0
 假设最终返回了True,则一定保证不会再检验时跳出换句话说就是所有的边满足了:
 
     
      
       
        
         
          v
         
         
          i
         
        
        
         .
        
        
         d
        
        
         ≤
        
        
         
          v
         
         
          
           i
          
          
           −
          
          
           1
          
         
        
        
         .
        
        
         d
        
        
         +
        
        
         w
        
        
         
          (
         
         
          
           v
          
          
           
            i
           
           
            −
           
           
            1
           
          
         
         
          ,
         
         
          
           v
          
          
           i
          
         
         
          )
         
        
       
       
         v_{i} . d \leq v_{i-1} . d+w\left(v_{i-1}, v_{i}\right) 
       
      
     vi.d≤vi−1.d+w(vi−1,vi)
 对其进行加和处理:
 
     
      
       
        
         
          
           
            
             
              ∑
             
             
              
               i
              
              
               =
              
              
               1
              
             
             
              k
             
            
            
             
              v
             
             
              i
             
            
            
             ⋅
            
            
             d
            
           
          
         
         
          
           
            
            
             ≤
            
            
             
              ∑
             
             
              
               i
              
              
               =
              
              
               1
              
             
             
              k
             
            
            
             
              (
             
             
              
               v
              
              
               
                i
               
               
                −
               
               
                1
               
              
             
             
              ⋅
             
             
              d
             
             
              +
             
             
              w
             
             
              
               (
              
              
               
                v
               
               
                
                 i
                
                
                 −
                
                
                 1
                
               
              
              
               ,
              
              
               
                v
               
               
                i
               
              
              
               )
              
             
             
              )
             
            
           
          
         
        
        
         
          
           
          
         
         
          
           
            
            
             =
            
            
             
              ∑
             
             
              
               i
              
              
               =
              
              
               1
              
             
             
              k
             
            
            
             
              v
             
             
              
               i
              
              
               −
              
              
               1
              
             
            
            
             ⋅
            
            
             d
            
            
             +
            
            
             
              ∑
             
             
              
               i
              
              
               =
              
              
               1
              
             
             
              k
             
            
            
             w
            
            
             
              (
             
             
              
               v
              
              
               
                i
               
               
                −
               
               
                1
               
              
             
             
              ,
             
             
              
               v
              
              
               i
              
             
             
              )
             
            
           
          
         
        
       
       
         \begin{aligned} \sum_{i=1}^{k} v_{i} \cdot d & \leq \sum_{i=1}^{k}\left(v_{i-1} \cdot d+w\left(v_{i-1}, v_{i}\right)\right) \\ &=\sum_{i=1}^{k} v_{i-1} \cdot d+\sum_{i=1}^{k} w\left(v_{i-1}, v_{i}\right) \end{aligned} 
       
      
     i=1∑kvi⋅d≤i=1∑k(vi−1⋅d+w(vi−1,vi))=i=1∑kvi−1⋅d+i=1∑kw(vi−1,vi)
 可以明显看出两个
    
     
      
       
        v
       
       
        .
       
       
        d
       
      
      
       v.d
      
     
    v.d的加和函数是错位的,但是由于
    
     
      
       
        
         v
        
        
         0
        
       
       
        =
       
       
        
         v
        
        
         k
        
       
      
      
       v_0=v_k
      
     
    v0=vk,那么上式可以抵消两个对属性d的求和函数得到:
 
     
      
       
        
         0
        
        
         ≤
        
        
         
          ∑
         
         
          
           i
          
          
           =
          
          
           1
          
         
         
          k
         
        
        
         w
        
        
         
          (
         
         
          
           v
          
          
           
            i
           
           
            −
           
           
            1
           
          
         
         
          ,
         
         
          
           v
          
          
           i
          
         
         
          )
         
        
       
       
         0 \leq \sum_{i=1}^{k} w\left(v_{i-1}, v_{i}\right) 
       
      
     0≤i=1∑kw(vi−1,vi)
 可以看到与最初的结论相矛盾,所以该问题得证。










