• Bellman-Ford(可解决负权边)--时间复杂度优化


    Bellman-Ford 可解决带有负权边的最短路问题

      解决负权边和Dijkstra相比是一个优点,Bellman-Ford的核心代码只有4行::

    u[],v[],w[] 分别存一条边的顶点、权值,dis[]存从 1 源点到各个顶点的距离

     

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

     

      愿过程:

          循环n-1次,把每个顶点每条边都松弛;

         优化方法:

          ①,最坏的情况就是循环了n-1次才求出到每个顶点的最短路径,若果在n-1次之前就已经全部松弛完成,那么后面的循环就是多余

            优化:

    for(k=1;k<=n-1;k++)//共有 n 个顶点,循环n-1次即可
    {
            flag = 0;
            for(i=1;i<=m;i++)//对当前所有的边进行松弛
            {
                if(dis[v[i]] > dis[u[i]]+w[i])
                {
                    dis[v[i]] = dis[u[i]]+w[i];
                    flag = 1;
                }
            }
            if(flag == 0) break;//松弛也完成,结束
    }

          ②,原过程:每次循环松弛过后,都有已经确定了的源点到某点最短的路径,此后这些顶点的最短路的值就会一直保持不变,不再受后续松弛操作的影响,但是每次还要判断是否需要松弛,这里浪费了时间。

         优化:确定一条边后总边数减一,把不能进行本次松弛的边再次后头存到原数组,松弛成功的边舍弃,再次松弛时只对未松弛的边进行操作,m的值会随着松弛预越来越小,直到全部完成。

    for(k=1;k<=n-1;k++)//共有 n 个顶点,循环n-1次即可
        {
            m = M;//重新赋值后的 m 是数组中存储边的条数
            s = 1;flag = 0;
            for(i=1;i<=m;i++)//对当前所有的边进行松弛
            {
                if(dis[v[i]] > dis[u[i]]+w[i])
                {
                    dis[v[i]] = dis[u[i]]+w[i];
                    M--;    //松弛成功,边数减一
                    flag = 1;
                }
                else//把本次不能进行松弛的边重新存储到当前的数组
                {
                    u[s] = u[i];
                    v[s] = v[i];
                    w[s] = w[i];
                    s++;
                }
            }
            if(flag == 0) break;//松弛也完成,结束
        }

    附完整代码:

    #include <stdio.h>
    int main()
    {
        int dis[10],i,k,m,n,s=1,u[10],v[10],w[10],M,flag;
        int inf = 99999999;
        scanf("%d%d",&n,&m);
        M = m;
        for(i=1;i<=m;i++)
        {
            scanf("%d%d%d",&u[i],&v[i],&w[i]);//输入各边及权值
        }
        for(i=1;i<=n;i++)
        {
            dis[i] = inf;//初始化为正无穷
        }
        dis[1] = 0;//以 1 为源点
    
        for(k=1;k<=n-1;k++)//共有 n 个顶点,循环n-1次即可
        {
            m = M;//重新赋值后的 m 是数组中存储边的条数
            s = 1;flag = 0;
            for(i=1;i<=m;i++)//对当前所有的边进行松弛
            {
                if(dis[v[i]] > dis[u[i]]+w[i])
                {
                    dis[v[i]] = dis[u[i]]+w[i];
                    M--;    //松弛成功,边数减一
                    flag = 1;
                }
                else//把本次不能进行松弛的边重新存储到当前的数组
                {
                    u[s] = u[i];
                    v[s] = v[i];
                    w[s] = w[i];
                    s++;
                }
            }
            if(flag == 0) break;//松弛也完成,结束
        }
    
        for(i=1;i<=n;i++)
        {
            printf("%d ",dis[i]);
        }
        return 0;
    }

     

     测试数据1:

    5 5
    2 3 2
    1 2 -3
    1 5 5
    4 5 2
    3 4 3

    运行结果:

    0 -3 -1 2 4

    测试数据2:

    5 7
    1 2 2
    1 5 10
    2 3 3
    2 5 7
    3 4 4
    4 5 5
    5 3 6

    运行结果:

    0 2 5 9 9

     

     

  • 相关阅读:
    javamail发送邮件
    java复制文件夹中的所有文件和文件夹到另一个文件夹中
    jsp中使用out和response.getOutputStream的方法
    PHP数组用法
    PHP中的session
    java中list按照某个属性排序方法
    java读取xml文件内容
    C#实现验证码
    java实现验证码功能
    Java IO(四)------字节输入输出流
  • 原文地址:https://www.cnblogs.com/ldy-miss/p/5658363.html
Copyright © 2020-2023  润新知