zoukankan      html  css  js  c++  java
  • SPFA算法——最短路径

    推荐技术公众号:不爱睡觉的大猪

     

    粗略讲讲SPFA算法的原理,SPFA算法是1994年西安交通大学段凡丁提出

    是一种求单源最短路的算法

    算法中需要用到的主要变量

    int n;  //表示n个点,从1到n标号

    int s,t;  //s为源点,t为终点

    int d[N];  //d[i]表示源点s到点i的最短路

    int p[N];  //记录路径(或者说记录前驱)

    queue <int> q;  //一个队列,用STL实现,当然可有手打队列,无所谓

    bool vis[N];   //vis[i]=1表示点i在队列中 vis[i]=0表示不在队列中

     

    几乎所有的最短路算法其步骤都可以分为两步

    1.初始化

    2.松弛操作

     

    初始化: d数组全部赋值为INF(无穷大);p数组全部赋值为s(即源点),或者赋值为-1,表示还没有知道前驱

                 然后d[s]=0;  表示源点不用求最短路径,或者说最短路就是0。将源点入队;

        (另外记住在整个算法中有顶点入队了要记得标记vis数组,有顶点出队了记得消除那个标记)

    队列+松弛操作

    读取队头顶点u,并将队头顶点u出队(记得消除标记);将与点u相连的所有点v进行松弛操作,如果能更新估计值(即令d[v]变小),那么就更新,另外,如果点v没有在队列中,那么要将点v入队(记得标记),如果已经在队列中了,那么就不用入队

    以此循环,直到队空为止就完成了单源最短路的求解

     

    SPFA可以处理负权边

    定理: 只要最短路径存在,上述SPFA算法必定能求出最小值。

    证明:

      每次将点放入队尾,都是经过松弛操作达到的。换言之,每次的优化将会有某个点v的最短路径估计值d[v]变小。所以算法的执行会使d越来越小。由于我们假定图中不存在负权回路,所以每个结点都有最短路径值。因此,算法不会无限执行下去,随着d值的逐渐变小,直到到达最短路径值时,算法结束,这时的最短路径估计值就是对应结点的最短路径值。(证毕)

    期望的时间复杂度O(ke), 其中k为所有顶点进队的平均次数,可以证明k一般小于等于2。

    判断有无负环:

      如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图)

     

     

     

    SPFA的两种写法,bfs和dfs,bfs判别负环不稳定,相当于限深度搜索,但是设置得好的话还是没问题的,dfs的话判断负环很快

    int spfa_bfs(int s)
    {
        queue <int> q;
        memset(d,0x3f,sizeof(d));
        d[s]=0;
        memset(c,0,sizeof(c));
        memset(vis,0,sizeof(vis));
    
        q.push(s);  vis[s]=1; c[s]=1;
        //顶点入队vis要做标记,另外要统计顶点的入队次数
        int OK=1;
        while(!q.empty())
        {
            int x;
            x=q.front(); q.pop();  vis[x]=0;
            //队头元素出队,并且消除标记
            for(int k=f[x]; k!=0; k=nnext[k]) //遍历顶点x的邻接表
            {
                int y=v[k];
                if( d[x]+w[k] < d[y])
                {
                    d[y]=d[x]+w[k];  //松弛
                    if(!vis[y])  //顶点y不在队内
                    {
                        vis[y]=1;    //标记
                        c[y]++;      //统计次数
                        q.push(y);   //入队
                        if(c[y]>NN)  //超过入队次数上限,说明有负环
                            return OK=0;
                    }
                }
            }
        }
    
        return OK;
    
    }
    int spfa_dfs(int u)
    {
        vis[u]=1;
        for(int k=f[u]; k!=0; k=e[k].next)
        {
            int v=e[k].v,w=e[k].w;
            if( d[u]+w < d[v] )
            {
                d[v]=d[u]+w;
                if(!vis[v])
                {
                    if(spfa_dfs(v))
                        return 1;
                }
                else
                    return 1;
            }
        }
        vis[u]=0;
        return 0;
    }

     

  • 相关阅读:
    Python 安装Twisted 提示python version 2.7 required,which was not found in the registry
    Openfire Strophe开发中文乱码问题
    css div 垂直居中
    How to create custom methods for use in spring security expression language annotations
    How to check “hasRole” in Java Code with Spring Security?
    Android 显示/隐藏 应用图标
    Android 当媒体变更后,通知其他应用重新扫描
    文件上传那些事儿
    专题:点滴Javascript
    主流动画实现方式总结
  • 原文地址:https://www.cnblogs.com/scau20110726/p/2776124.html
Copyright © 2011-2022 走看看