zoukankan      html  css  js  c++  java
  • 最短路四大算法证明以及分析(Flord Bijkstra Bellman-ford SPFA)

    此博文不具体给出其算法的代码,只对其中算法进行分并且给予证明

    PS:这些算法我不用证明都是它是正确的(上世纪的数学家看着这些都不用证明,为啥,很简单的),但是我坚持重新证明一遍实际是为了加深印象,并且理解其中的道理和思想,这样在以后的运用中才能灵活运用,当然证明这些算法也


    算法一:Flord 算法,也是传说中的只用五行就可以解决的多源最短路径问题

    采用邻接矩阵来储存图,时间复杂度为O(n^3),能解决含正权,负权的最短路径,不能解决含有负的最短路径(负环也没有最短路径)

    for(k=1;k<=n;k++)
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
                if(e[i][j]>e[i][k]+e[k][j])
                     e[i][j]=e[i][k]+e[k][j];

    当k=1时进行一个嵌套的for循环求出来的是任意两点之间只允许经过点1的最短路径,这个都可以理解吧。

    ( ̄▽ ̄)"

    当k=2时,经过一个if else的判断最终求出来的是任意两个顶点之间只允许经过1 2号顶点的最短路径。

    这时候可能有人说在进行 if      else 之后判断的是i->j通过2号顶点路径后路径能否缩小,那么如果i->j还能通过1号顶点再次缩小路径,那还能体现出来吗?

    好的下面咱们来证明

    假设咱们判断过之后i->j之间通过2号顶点可以缩小路径,此时i与j的路径为i->2->j,假设还可以通过1号顶点缩小路径(也就是上面所说的情况)

    此时1号顶点肯定在i->j之间,也就是i->2或2->j之间 ,而咱们在第一次for循环之后的意义上面已经说过了,即任意两点可以经过1号顶点后更新的最短路径,那么回过来即i->2之间或者2->j之间若可以经过1号顶点在缩短路径,那么铁定在第一次循环后就已经在i->2或2->j之间添加1号顶点了,所以上面的担心是多余的(●'◡'●)

    当k=2的循环结束后,得到的意义便是任意两个顶点只允许经过1 2号顶点的最短路径


    当k=n时  循环结束后便是任意两点之间允许经过1~n个顶点后的最短路径,(如果有疑问,那么再按着刚刚在2号顶点的证明过程比较一下)



    算法二:dijkstra算法 (求单源最短路径,只允许正权)

    这个是比较常用的算法,其常用的储存图的方式有邻接矩阵,邻接表两种。采用邻接矩阵的时间复杂度为O(n^2)

    在此用邻接矩阵来举例子

    先说一下算法原理,初始化就不说了吧(任意两点之间之间路径为INF,自己与自己的路径为0)随后输入图。

    用一个dis[]数组记录源点到其他点的直接路径。假设源点号为S,并且用数组book[]来表示s->其他点的最短路径是否确定,确定的点令其值为1(book[s]=1)。

    然后从dis[]数组找s->v最小的值,假设为这个顶点为u,那么s->u的最短路径肯定就确定了。为啥 ?因为s->u若再经过其他点所成的路径一定比s->u的路径的大。

    已经确定s->u是最短路径后,松弛u点到其他点的路径,令book[u]=1。(这点是关键)

    所以算法分三个步骤

    1.然后再从dis[]从最短路径未确定的路径找出的最小值,令其为确定的最短路径(假设这个顶点是u),令book[u]=1;(证明下面在说)

    2.从u点松弛

    3.重复以上1. 2点直至所有点的最短路径确定。



    也许有很多人对第1步有些不解。为什么从中找出的符合条件的u的最短路径一定确定了呢?别急下面来证明


    看这个图

    第一次  找出s->a最小,那么令此时s->a的路径为最短路径,令book[2]=1,(这个都没疑问吧)...... 然后松弛a->b得到mid=dis[a]+map[a][b]<dis[b]   且松弛之后s->b的路径变小了

    第二次,再次按步骤1找出最小的为dis[b],此时的dis[b]为s->b的最短路径。为啥?假设此时dis[b]的值不是s->b的最短路径,那么要想找s->b的更小路径至少中转至一个顶点v,且s->v的距离满足dis[v]<dis[b](不然路径会更大),然后经v中转至b,但是因为dsi[v]<dis[b]表明v已经确定为最小路径了,前面咱们说过,确定为最短路径的点一定要经过此点到其他顶点的松弛,所以你找的所有的s->b的路径一定>=dis[b],

    也就是下面这个原理


    u为此时经过步骤1确定的点,假设s->u的距离不是最短路径,即存在另一条路径s->v->u(你也可以假设s->v之间还有许多点,但是我们要确保v->u是直接的,即不经过任何一个中转点)使得路径长度mid<dis[u],

    又因为假设的路径中v是在s->u之间,所以v的最短路径已经确定且v经过松弛,即v到任何点的都经过松弛(包括点u),所以此时我们找的mid是一定>=dis[u]的,即假设不成立。               

    所以你就默认他们是最短路径这一事实吧( ̄▽ ̄)"

    代码:

    附上dijkstr算法


    /*
    dijkstr 算法
    二步:
    1.初始化
    2.每次找出dis最小值则为确定对短路
    注意:minn=INF 或者找到答案这一提前跳出
    */
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<string>
    #include<iostream>
    #include<cstdlib>
    #define N 1010
    #define INF 0x3f3f3f3f
    #define MOD 1000000007
    #define WC 1e-6
    typedef long long LL;
    using namespace std;
    int book[N],dis[N],map[N][N];
    void dijkstr(int n)
    {
        int t=n,minn,u,to;
        memset(book,0,sizeof(book));
        for(int i=1;i<=n;i++)
            dis[i]=INF;
        dis[1]=0;
        while(t--)
        {
            minn=INF;
            for(int i=1;i<=n;i++)
            {
                if(!book[i]&&dis[i]<minn)
                {
                    u=i;
                    minn=dis[i];
                }
            }
            book[u]=1;
            for(int i=1;i<=n;i++)//松弛
            {
                if(!book[i])
                    {
                        to=dis[u]+map[u][i];
                        if(to<dis[i])
                            dis[i]=to;
                    }
            }
        }
    }
    int main()
    {
        //cin map 就OK
    }
    





    有时间再补充另两个算法的证明


    附上SPFA代码

    /*
    SPFA
    三步
    1.初始信息 将初始点加入队列
    2.每次从队列的第一个点进行松弛,可以更新另一个点则将其加入队列(已经加入队列则不需要再次加入)
    2.队列为空结束
    */
    
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<string>
    #include<iostream>
    #include<cstdlib>
    #include<queue>
    #define N 1010
    #define INF 0x3f3f3f3f
    #define WC 1e-6
    typedef long long LL;
    using namespace std;
    int book[N],dis[N],map[N][N];//存信息
    queue<int>mmp;
    void SPFA(int n)//n得出范围  输入map就OK
    {
        int to;
        memset(book,0,sizeof(book));
        for(int i=1;i<=n;i++)
            dis[i]=INF;
        dis[1]=0;
        while(!mmp.empty())
            mmp.pop();
        mmp.push(1);
        while(!mmp.empty())
        {
            int u=mmp.front();//出队伍
            mmp.pop();
            book[u]=0;
            for(int i=1;i<=n;i++)//松弛
            {
                to=dis[u]+map[u][i];
                if(to<dis[i])
                {
                    dis[i]=to;
                    if(!book[i])
                    {
                        mmp.push(i);
                        book[i]=1;
                    }
                }
            }
        }
    }
    int main()
    {
        //cin map
    }
    
    
    
    




    附上bellman算法

    /*
    bellman算法   可以判断负环
    1.初始化dis (struct)edge
    2.遍历所有边
    2.更新成功则进行下一次
    */
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<string>
    #include<iostream>
    #include<cstdlib>
    #define N 4010
    #define INF 0x3f3f3f3f
    #define WC 1e-6
    typedef long long LL;
    using namespace std;
    int dis[N];
    struct node{
    int u,v,w;
    }e[N];
    void bellman(int n,int cnt)
    {
        int to,t,flag;
        for(int i=2;i<=n;i++)
            dis[i]=INF;
        dis[1]=0;
        t=n-1;
        while(t--)
        {
            flag=0;
            for(int i=0;i<cnt;i++)
            {
                to=dis[e[i].u]+e[i].w;
                if(to<dis[e[i].v])
                {
                    dis[e[i].v]=to;
                    flag=1;
                }
            }
            if(!flag)
                break;
        }
    //    if(flag)//最后一次用于判断负环
    }
    int main()
    {
       //cin edge
    }


  • 相关阅读:
    ListView点击事件
    ListView优化:
    自定义ListView
    ListView简单使用
    mysql中show processlist过滤和杀死线程
    自定义控件
    yum配置中driver-class-name: com.mysql.jdbc.Driver报错
    CSS+HTML
    maven的配置
    Model、ModelMap、ModelAndView的作用及区别
  • 原文地址:https://www.cnblogs.com/dchnzlh/p/9780080.html
Copyright © 2011-2022 走看看