bellman-ford算法 最短路

重要应用:在负权的图的单源最短路问题

Bellman-Ford 算法和 Dijkstra 算法都是可以解决单源最短路径的算法,一个实现的很好的 Dijkstra 算法比 Bellman-Ford 算法的运行时间要低,但dijkstra算法无法解决存在负权环的图的单源最短路问题(因为dijkstra算法每循环一次,确定一条到某个顶点的最短路,之所以能确定,是因为每条边都是正值,若到达其他顶点的最短路经过该顶点,则最短路总权值会更大)。

Bellman-Ford 算法描述:

如求下图单源最短路径,以1为源点,求到各顶点的最短路径,黑色代表权值,红色代表第几条边(是第几条边在实际中会不同,这里是假设 )
这里写图片描述
1.初始化:
将除源点外的所有顶点的最短距离估计值 d[v] =+∞,源顶点距离为 0;
这里写图片描述
2.迭代求解计算最短路径:
执行 V - 1 次 (遍历一条最短路径经过的点最多是v-1个嘛,所有松弛|v|-1次后最短路必然会出现!);
反复对边集e中的每条边进行松弛操作,(即如果起点 u 的距离 d 加上边的权值 w 小于终点 v 的距离 d,则更新终点 v 的距离值 d),使得顶点集V中的每个顶点v的最短距离估计值逐步逼近其最短距离;

第一次循环:
从第一条边开始到第十条边进行松弛操作;
这里写图片描述
得到下图:
这里写图片描述
第二次循环:
也是从第一条边开始到第十条边进行松弛操作;与上面同理;
得到如图;
这里写图片描述
第三次循环:
也是从第一条边开始到第十条边进行松弛操作;与上面同理;
因为第三次循环发现任何没有任何一个更新,说明到各个顶点的最短路都已经找到,break;跳出循环,结束

3.检验负权回路:
遍历图中的所有边,计算 u 至 v 的距离,如果对于 v 存在更小的距离,则说明存在环(因为经过上面步骤二, 如果不存在负环,则到各点的最短路已经确定);

     for(i=1;i<=enum;i++) //检查有无负权环  
     {  
          if(d[edge[i].v]>dis[e[i].u]+edge[i].w)  
          {  
               flag = false;//存在负环  
               break;  
          }  
     } 

代码:无路径

#include<stdio.h>
#include<string.h>
#define Max 99999
int m,n,d[10000];//n条边 
struct Edge{
    int u;
    int v;
    int w;
}e[10000];

bellmenford(int root){
    int i,j,k;
    for(i=1;i<=m;i++){
        d[i]=Max;
    }
    d[root]=0;
    for(k=0;k<m-1;k++){//循环k-1次 
        for(i=1;i<=n;i++){//循环每条边 
            if(d[e[i].v]>d[e[i].u]+e[i].w){//如果起点 u 的距离 d 加上边的权值 w 小于终点 v 的距离 d,则更新终点 v 的距离值 d
                d[e[i].v]=d[e[i].u]+e[i].w;
            }
        } 
    }
    for(i=1;i<=n;i++){//判断负环 
        if(d[e[i].v]>d[e[i].u]+e[i].w){
            printf("NO");
            return 0;
        }
    }
    for(i=1;i<=m;i++){
        printf("%d ",d[i]);
    }
}

int main(){
    int i,j,k=1;
    scanf("%d%d",&m,&n);
    for(i=1;i<=n;i++){
        scanf("%d%d%d",&e[k].u,&e[k].v,&e[k].w);
        k++;
    }
    bellmenford(1);
    return 0;
} 

有路径输出:

#include<stdio.h>
#include<string.h>
#define Max 99999
int m,n,d[10000],pre[10000];//n条边,d[]表示到某顶点的总权值,pre[]记录路径 
struct Edge{
    int u;
    int v;
    int w;
}e[10000];

bellmenford(int root){
    int i,j,k;
    for(i=1;i<=m;i++){
        d[i]=Max;
        pre[i]=root;//初始化到各顶点的路径的起点为源点 
    }
    d[root]=0;
    for(k=0;k<m-1;k++){//循环k-1次 
        for(i=1;i<=n;i++){//循环每条边 
            if(d[e[i].v]>d[e[i].u]+e[i].w){//如果起点 u 的距离 d 加上边的权值 w 小于终点 v 的距离 d,则更新终点 v 的距离值 d
                d[e[i].v]=d[e[i].u]+e[i].w;
                pre[e[i].v]=e[i].u;//到v点经过u点 
            }
        } 
    }
    for(i=1;i<=n;i++){//判断负环 
        if(d[e[i].v]>d[e[i].u]+e[i].w){
            printf("NO");
            return 0;
        }
    }
    for(i=1;i<=m;i++){
        printf("%d %d ",d[i],i);
        j=i;
        while(pre[j]!=root){
            printf("%d ",pre[j]);
            j=pre[j];
        }
        if(i!=root)printf("%d",root);
        printf("\n");
    }
}

int main(){
    int i,j,k=1;
    scanf("%d%d",&m,&n);
    for(i=1;i<=n;i++){
        scanf("%d%d%d",&e[k].u,&e[k].v,&e[k].w);
        k++;
    }
    bellmenford(1);
    return 0;
} 
  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值