6.3 最短路径:Bellman-Ford算法

Bellman-Ford算法

很强的一个算法,无论是思路、思想、代码实现都很优秀
而且,它可以解决负权边的问题

简介

一句话概括这个算法就是:“对所有的边进行n-1次松弛操作”
一样的,我们用uvw三者表示“从顶点u[i]到顶点v[i]的这条边,权值为w[i]”
随后检查,新的距离会不会比原本的距离短
1.用dis数组初始化估计值,并且把除了起始点之外的,都设置为正无穷大(解释见后)
2.按边的图的数组中的顺序,遍历检查"这条路会不会距离变短"
3.由于起始点到自身的距离是0,所以一定存在它到相邻区域的更小值,于是更新

比如到2的预设值是正无穷,而有一条(u=1,v=2,w=-3)的路,那么这个正无穷就会被更新为-3
4.重复2-3步,不断得到新的结果
5.返回最终结果

我们实验发现,可以知道“在第一轮后,实际上得到的是'从一号顶点,只经过一条边,到其余点的最短路径'”
而实际2-3的重复,就是利用“一次次的松弛”,达到对条件 “只经过k条边”的递增

最终,只需要进行n-1轮,就可以了。再包含n个顶点的图中,两点之间的最短路径最多包含n-1个边
(而且不含回路:正回路的距离会越来越远,负回路不可能有最短路径,因为它会一直一直递减)

实现

Bellman-Ford算法的核心四行代码如下,体现了我们上文所说的操作:

    for(k=1;k<=n-1;k++)
        for(i=1;i<=m;i++)
            if(dis[v[i]] > dis[u[i]] + w[i])
                dis[v[i]] = dis[u[i]] + w[i]

其中,n是点个数,m是边个数

Bellman-Ford算法还可以检测负权回路,如果在n-1次松弛后,最短路径还在变化,那就是有负权回路了
具体代码实现如下:

    flag = 0 ;
    for(i=1;i<=m;i++)
        if(dis[v[i]] > dis[u[i]] + w[i])
            flag = 1 ;


    if(flag == 1)
        printf("此图包含负权回路") ;

代码示例

Bellman-Ford算法的完整代码如下,实质上还有地方可以优化
优化的地方可以单独列为一小节,我们这里先给出最初的算法代码:

    #include <stdio.h>
    int main()
    {
        int dis[10],bal[10],i,k,n,m,u[10],v[10],w[10],check,flag ; 
        int inf = 99999999 ; //用inf存储我们认为的正无穷值 
        
        //读入n和m,n表示顶点个数,m表示边的条数    
        scanf("%d %d",&n,&m);

        //读入边
        for(i = 1 ; i< m ; i++){
            scanf("%d %d %d",&u[i],&v[i],&w[i]);s
        }

        //初始化dis数组,这里是1号顶点到其余各个顶点的初始路程  
        for(i=1;i<=n;i++)
            dis[i] = inf ;
        dis[1] = 0 ;//我们这里把1作为起始点

        //开始Bellman-Ford算法
        for(k=1;k<=n-1;k++)
        {
            check = 0 ; //用来标记本轮松弛中数组的dis是否会发生更新

            //进行一轮松弛
            for(i=1;i<=m;i++)
            {
                if(dis[v[i]] > dis[u[i]] + w[i])
                {
                    dis[v[i]] = dis[u[i]] + w[i] ;
                    check = 1 ;
                }
                //松弛完毕后检测数组dis是否有更新
            }
        }

            //检测负权回路
            flag = 0 ;
            for(i=1;i<=m;i++)
                if(dis[v[i]] > dis[u[i]] + w[i])
                    flag = 1 ;

            if(flag == 1)
                print("此图存在负权回路");
            else{
                //输出最终结果(无负权回路)
                for(i=1;i<=n;i++)
                    printf("%d" , dis[i]);
            }
        

        getchar();getchar();
        return 0 ;
    }
posted @ 2021-11-19 11:06  RetenQ  阅读(43)  评论(0编辑  收藏  举报