最短路径问题 3.Bellman-Ford算法

简要:Bellman-Ford算法计算的仍然是从一个点到其他所有点的最短路径算法,其时间复杂度是O(NE),N表示点数,E表示边数,不难看出,当一个图稍微稠密一点,边的数量会超过点数那么实际上效率是低于Dijkstra算法的。但是本算法可以计算存在负权边的情况(不存在负回路),因此可以用于更广泛的情况,但其实在日常解题应用中我们基本不会用到该算法,因为有一个比它效率更高的算法即SPFA算法,下一章会介绍到。SPFA算法其实是从Bellman-Ford算法演变而来的,那么从基础的开始,我们先来理解一下Bellman-Ford算法。

算法描述:s为起点,dis[v]为s到v的最短距离,pre[v]是v的前驱结点,w[j]是边 j 的长度,j 边的起点和终点分别是u,v。

1、初始化:dis[s]=0, dis[v]=∞(v≠s), pre[s]=0

2、for(i=1;i<=n-1;i++)

       for(j=1;j<=e;j++)

         if(dis[u]+w[j]<dis[v]){

            dis[v]=dis[u]+w[j];

            pre[v]=u;

           }

算法理解:一开始已标记的点只有起点,每一次枚举所有的边,总会有一些边连接着已标记的点和未标记的点,即已经计算过最短距离和为计算过最短距离的点。因此每次枚举都会更新一些未标记的点成为已标记的点。而n-1次则保证了最坏情况下所有的点均可以被标记,即图的样子是一“串”的情况。

对于负权回路的情况,因为每一次枚举都会走过一圈负权回路,那么恰好在该回路两边的点之间的最短距离就会无限减小,因此会造成错误。对此,大家给出了一个不是解决方法的解决方法,就是如果在两重循环结束后,再次枚举每条边,如果再次出现某两点的距离减少,那么就返回"错误",已表示有负权回路,无法算出答案。

当然,对于负权回路也有解决方法,在介绍完SPFA算法后会补充一下。

代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int n,e,s;
struct node{
    int x;
    int y;
    int val;
}m[105];
int dis[105],pre[105],a,b,w[105][105];
int bellmanford(int s){
     int i,j;
     
     for(i=1;i<=n;i++)
      dis[i]=w[s][i];
    dis[s]=0;pre[s]=0;
     for(i=1;i<=n-1;i++)
      for(j=1;j<=e;j++)
           if(dis[m[j].x]+m[j].val<dis[m[j].y])
           {
           dis[m[j].y]=dis[m[j].x]+m[j].val;
        pre[m[j].y]=m[j].x;    
           }
     for(j=1;j<=e;j++){
         if(dis[m[j].x]+m[j].val<dis[m[j].y]) return 0;
     }
     return dis[5];
         
}
int main(){
    int i,j;
    scanf("%d%d",&n,&e);
    memset(dis,100,sizeof(dis));
    memset(w,100,sizeof(w));
     for(i=1;i<=e;i++){
        scanf("%d%d%d",&m[i].x,&m[i].y,&m[i].val);
        a=m[i].x;
        b=m[i].y;
        w[a][b]=m[i].val;
    }
    for(i=1;i<=5;i++)
     for(j=1;j<=5;j++)
      printf("%d ",w[i][j]);
    scanf("%d",&s);
    printf("%d",bellmanford(s));
    return 0;
} 

 

posted @ 2018-04-18 22:19  uncklesam7  阅读(226)  评论(0编辑  收藏  举报