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

    SPFA:Shortest Path Faster Algoriithm 快速最短路径算法

    SPFA的核心算法就是Bell-Ford算法。它使用一个队列或者一个栈来减少了Bell-Ford中不必要的松弛。可以处理负边和环的情况,他的使用比Dijstra更广泛。但是未经优化的SPFA算法时间复杂度不稳定,测试用例对它的影响较大。所以有的时候为了简单方便,我们直接使用Dijstra算法来解决。

       SPFA实现的过程非常类似于BFS。他们都是使用队列来维护,不同的是,BFS的每个顶点只入队一次,而SPFA的结点则课能会入队多次。所以我们需要一个数组bool vis[] 来记录该顶点是否在队列中。SPFA实现过程如下:

          步骤一: 首先将将源点s入队,将所有点到s的距离设置为dis[i]=∞,dis[s]=0

          步骤二:如果队列为空,算法终止。否则取出队首元素u,将与u相连的边<u,v>进行松弛,如果dis[u]距离更新了且u不在队列中,则将u入队,若u进队次数大于n(总顶点数),则说明有负环,立即结束算法。

          步骤三:不断地重复步骤二,直到算法结束

    SPFA算法的时间复杂度取决于顶点总共的入队次数,他的算法时间复杂度为O(ke),e是边数,k是每个节点平均入队的次数,一般有k<2.

    #include<queue>
    #define INF INT_MAX/2
    #define MAX_SIZE 1000
    int dis[MAX_SIZE];          //保存最短距离
    bool vis[MAX_SIZE];
    int a[MAX_SIZE][MAX_SIZE];  //邻接矩阵存储
    int times[MAX_SIZE];        //记录顶点入队次数
    bool SPFA(int s,int n) {  //有环返回1,无环返回0
        int i,v;
        memset(vis, 0, sizeof(vis));
        memset(times, 0, sizeof(times));
        queue<int>Q;                   //保存顶点的队列
        for (i = 0; i <n; i++)
            dis[i] = INF;       //初始化为无穷
        dis[s] = 0;             //源节点
        Q.push(s);                //源节点入队
        vis[s] = 1;          //源节点在队列中,所以标记为1
        times[s]++;                       
        while (!Q.empty()){        //如果队列非空
            v = Q.front();         //取出队列一个元素
            Q.pop();
            vis[v] = 0;        //将该点标记为不在队列中
            for (i = 0; i < n; i++) {                   //松弛该点的邻边
                if (dis[i] > a[v][i] + dis[v]) {
                    dis[i] = a[v][i] + dis[v];      //松弛一把更新距离
                    if (!vis[i]) {         //如果被松弛的顶点i不在队列中
                        vis[i] = 1;        //加入队列并标记
                        Q.push(i);
                        if (++times[i] > n) {   //入队次数加1次,如果入队次数超过n的话,表示有环
                           return 1;
                        }
                    }
                }
            }
        }
        return 0;      
    }

     SPFA算法有两个优化策略SLF和LLL——SLF:Small Label First 策略,设要加入的节点是j,队首元素为i,若dist(j)<dist(i),则将j插入队首,否则插入队尾; LLL:Large Label Last 策略,设队首元素为i,队列中所有dist值的平均值为x,若dist(i)>x则将i插入到队尾,查找下一元素,直到找到某一i使得dist(i)<=x,则将i出队进行松弛操作。 SLF 可使速度提高 15 ~ 20%;SLF + LLL 可提高约 50%。 在实际的应用中SPFA的算法时间效率不是很稳定。下面是SLF优化代码,使用双端队列。

    #include<deque>
    #define INF INT_MAX/2
    #define MAX_SIZE 1000
    int dis[MAX_SIZE];          //保存最短距离
    bool vis[MAX_SIZE];
    int a[MAX_SIZE][MAX_SIZE];  //邻接矩阵存储
    int times[MAX_SIZE];        //记录顶点入队次数
    bool SPFA(int s,int n) {     //有环返回1,无环返回0
    	int i,v;        
    	memset(vis, 0, sizeof(vis));
    	memset(times, 0, sizeof(times));
    	deque<int>deq;                   //保存顶点的队列
    	for (i = 0; i <n; i++)
    		dis[i] = INF;       //初始化为无穷
    	dis[s] = 0;             //源节点
    	deq.push_back(s);       //源节点入队
    	vis[s] = 1;             //源节点在队列中,所以标记为1
    	times[s]++;                       
    	while (!deq.empty()){        //如果队列非空
    		v = deq.front();         //取出队列一个元素
    		deq.pop_front();         //出队
    		vis[v] = 0;        //将该点标记为不在队列中
    		for (i = 0; i < n; i++) {                   //松弛该点的邻边
    			if (dis[i] > a[v][i] + dis[v]) {
    				dis[i] = a[v][i] + dis[v];      //松弛一把更新距离
    				if (!vis[i]) {         //如果被松弛的顶点i不在队列中
    					vis[i] = 1;        //加入队列并标记
    					if (deq.empty()||i > deq.front()) /*一点小优化*/
    						deq.push_back(i);
    					else
    						deq.push_front(i); 
    					if (++times[i] > n)   //入队次数加1次,如果入队次数超过n的话,表示有环,返回1
    						return 1;
    				}
    			}
    		}
    	}
    	return 0;     
    }
    

      

  • 相关阅读:
    1136.NumberSteps
    1134.密码翻译
    1133.学分绩点
    1131.合唱队形
    1132.与7无关的数
    1130.日志排序
    Educational Codeforces Round 41 (Rated for Div. 2)
    Codeforces Round #378 (Div. 2) F
    Codeforces Round #290 (Div. 2)
    牛客网练习13 乌龟跑步
  • 原文地址:https://www.cnblogs.com/td15980891505/p/5405918.html
Copyright © 2011-2022 走看看