• 最短路


    http://acm.sdut.edu.cn/sdutoj/problem.php?action=showproblem&problemid=2894

    谈一下对贝尔曼福特的认识(参考别人的)

    BF是对边进行操作,dijkstra 是对点进行操作,N个顶点的最短路最多是N-1条边,所以需要循环N-1次

    1.初始化

    2.迭代求解:反复对边集中的每条边进行松弛操作,使得顶点集v中的每个顶点vde最短距离逐步逼近其最短距离,运行v-1次

    3:检验负权回路:判断边集中的每一条边的两个端点是否收敛,如果存在未瘦脸的顶点,则返回false,表明问题无解,否则算法返回true,并且从源点可达的顶点v的最短距离保存在的d【v】中。

     

     

    4描述性证明编辑

     

    首先指出,图的任意一条最短路径既不能包含负权回路,也不会包含正权回路,因此它最多包含|v|-1条边。

     

    其次,从源点s可达的所有顶点如果 存在最短路径,则这些最短路径构成一个以s为根的最短路径树。Bellman-Ford算法的迭代松弛操作,实际上就是按顶点距离s的层次,逐层生成这棵最短路径树的过程。

     

    在对每条边进行第1遍松弛的时候,生成了从s出发,层次至多为1的那些树枝。也就是说,找到了与s至多有1条边相联的那些顶点的最短路径;对每条边进行第2遍松弛的时候,生成了第2层次的树枝,就是说找到了经过2条边相连的那些顶点的最短路径……。因为最短路径最多只包含|v|-1 条边,所以,只需要循环|v|-1 次。

     

    每实施一次松弛操作最短路径树上就会有一层顶点达到其最短距离,此后这层顶点的最短距离值就会一直保持不变,不再受后续松弛操作的影响。(但是,每次还要判断松弛,这里浪费了大量的时间,怎么优化?单纯的优化是否可行?)

     

    注意:上述只对正权图有效。如果存在负权不一定第i次就能确定最短路,且与边的顺序有关。

     

    如果没有负权回路,由于最短路径树的高度最多只能是|v|-1,所以最多经过|v|-1遍松弛操作后,所有从s可达的顶点必将求出最短距离。如果 d[v]仍保持 +∞,则表明从s到v不可达。

     

    如果有负权回路,那么第 |v| 遍松弛操作仍然会成功,这时,负权回路上的顶点不会收敛。[1]

     

     

     

     

     

     
    Bellman-Ford算法可以大致分为三个部分
    第一,初始化所有点。每一个点保存一个值,表示从原点到达这个点的距离,将原点的值设为0,其它的点的值设为无穷大(表示不可达)。
    第二,进行循环,循环下标为从1到n-1(n等于图中点的个数)。在循环内部,遍历所有的边,进行松弛计算。
    第三,遍历途中所有的边(edge(u,v)),判断是否存在这样情况:
    d(v) > d (u) + w(u,v)
    则返回false,表示途中存在从源点可达的权为负的回路。
     
    之所以需要第三部分的原因,是因为,如果存在从源点可达的权为负的回路。则 应为无法收敛而导致不能求出最短路径。
    考虑如下的图:
     

     

    经过第一次遍历后,点B的值变为5,点C的值变为8,这时,注意权重为-10的边,这条边的存在,导致点A的值变为-2。(8+ -10=-2)
     
     

     

    第二次遍历后,点B的值变为3,点C变为6,点A变为-4。正是因为有一条负边在回路中,导致每次遍历后,各个点的值不断变小。
     
    在回过来看一下bellman-ford算法的第三部分,遍历所有边,检查是否存在d(v) > d (u) + w(u,v)。因为第二部分循环的次数是定长的,所以如果存在无法收敛的情况,则肯定能够在第三部分中检查出来。比如
     

     

    此时,点A的值为-2,点B的值为5,边AB的权重为5,5 > -2 + 5. 检查出来这条边没有收敛。
     
    所以,Bellman-Ford算法可以解决图中有权为负数的边的单源最短路径问。

     

     

    http://blog.csdn.net/u012860063/article/details/24492003

     

    // BF  贝尔曼福特代码模板,学习
    #include <stdio.h>
    #include <string.h>
    #define INF 999999
    struct node
    {
        int u,v,w;
    }q[4000002];
    int dis[500002];
    int t = 0;
    int n,m;
    int s,e;
    int u,v,w;
    int flag;
    void add(int u,int v,int w)
    {
        q[t].u = u;
        q[t].v = v;
        q[t++].w = w;
    }
    void BF()
    {
        int i,j;
        for(i = 0;i<=n;i++)
        {
            dis[i] = INF;
        }
        dis[s] = 0;
        for(i = 1;i<=n-1;i++)
        {
            flag = 0;
            for(j = 0;j<t;j++)
            {
                if(dis[q[j].v]>dis[q[j].u] + q[j].w)
                {
                    dis[q[j].v] = dis[q[j].u] + q[j].w;
                    flag = 1;
                }
            }
            if(flag == 0)
            break;
        }
        printf("%d
    ",dis[e]);
    }
    int main()
    {
        int i = 0;
        while(scanf("%d%d",&n,&m)!=EOF)
        {
            for(i = 0;i<m;i++)
            {
                scanf("%d%d%d",&u,&v,&w);
                add(u,v,w);
                add(v,u,w);
            }
            scanf("%d%d",&s,&e);
            BF();
        }
        return 0;
    }
  • 相关阅读:
    rdlc报表动态生成实例
    动态分页实现
    多文件上传
    文件压缩
    javascript解决中文传递乱码和特殊字符问题
    rdlc报表动态生成公共类
    SQLHelp类
    验证码
    使用bison和yacc制作脚本语言(3)
    C Mingw gcc printf 刷新缓冲行
  • 原文地址:https://www.cnblogs.com/yangyongqian/p/3887072.html
Copyright © 2020-2023  润新知