关于SPFA的双端队列优化

7.11 Update

我做题的时候发现这样写会RE

因为在使用双端队列优化SPFA的时候 在将一个点加入队列的时候,如果队列已经空了 那么一旦出现dis[Q.front()]就会RE 可以这样修改

if(!Q.empty()) {
    if(dis[v[k]] < dis[Q.front()]) Q.push_front(v[k]);
    else Q.push_back(v[k]);
}
else Q.push_front(v[k]);

这样就不会RE了

 

期望时间复杂度:O(k*e或me)//k是增长很快的函数ackermann的反函数,2^65536次方也就5以下,但是可以被恶意数据卡掉,起复杂度就位(n*n ) //其中m为所有顶点进队的平均次数,可以证明m一般小于等于2n:“算法编程后实际运算情况表明m一般没有超过2n.事实上顶点入队次数m是一个不容易事先分析出来的数,但它确是一个随图的不同而略有不同的常数.所谓常数,就是与e无关,与n也无关,仅与边的权值分布有关.一旦图确定,权值确定,原点确定,m就是一个确定的常数.所以SPFA算法复杂度为O(e).证毕."(SPFA的论文)不过,这个证明是非常不严谨甚至错误的,事实上在bellman算法的论文中已有这方面的内容,所以国际上一般不承认SPFA算法。

 

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的算法时间效率不是很稳定,为了避免最坏情况的出现,通常使用效率更加稳定的Dijkstra算法

 

上面这两段话呢,来自百度。

关于SPFA的时间复杂度,地球人应该都知道非常玄学的,近似可以看作O(看脸),

对于上面所说的最坏情况,我还记得有一次考试的题目中有一道最短路问题。

那道题的最后一组数据是用来卡SPFA的,(吓,出题人好毒瘤

这里我们不介绍LLL优化,

Only SLF优化


 

在每一次松弛操作的时候都已进入队列的操作

可是朴素的SPFA中将元素放到队列中时无序的,

如果改用一种很吊的队列的话,将其中的元素变得有点儿顺序

就可以起到优化的作用

这便是SLF优化

我们使用c++STL中的deque来实现上述操作

建议Pascal选手尽快转C++吧

下面就是代码(可能会很丑哦)

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <deque>
#include <algorithm>

const int maxnode = 1e4+3;
const int maxedge = 5e5+3;

#define INF 2147483647

using namespace std;

deque<int> Q;

int first[maxnode], next[maxedge], n, m, s;
int u[maxedge], v[maxedge], w[maxedge], dis[maxnode];

bool vis[maxnode];

inline int read() {
    char c = getchar();
    int x = 0, f = 1;
    while (c < '0' || c > '9') {
        if(c == '-') f = -1;
        c = getchar();
    }
    while (c <= '9' && c >= '0') {
        x = x*10 + c-'0';
        c = getchar();
    }
    return x * f;
}

inline void addedge(int from, int i) {
    next[i] = first[from];
    first[from] = i;
}

inline void SPFA(int sta) {
    Q.push_back(sta), vis[sta] = true;
    while (!Q.empty()) {
        int x = Q.front();
        int k = first[x];
        Q.pop_front();
        while (k != -1) {
            if (dis[v[k]] >= dis[u[k]] + w[k]) {
                dis[v[k]] = dis[u[k]] + w[k];
                if (!vis[v[k]]) {
                    vis[v[k]] = 1;
                    if (dis[v[k]] < dis[Q.front()]) Q.push_front(v[k]);
                    else Q.push_back(v[k]);
                }
            }
            k = next[k];
        }
        vis[x] = 0;
    }
}

int main() {
    n = read(), m = read(), s = read();
    for (int i=1; i<=n; i++) dis[i] = INF;
    dis[s] = 0;
    memset(first, -1, sizeof(first));
    for (int i=1; i<=m; i++) {
        u[i] = read(), v[i] = read(), w[i] = read();
        addedge (u[i], i);
    }
    SPFA(s);
    for (int i=1; i<=n; i++) printf("%d ", dis[i]);
}

 

posted @ 2018-07-04 11:06  Mystical-W  阅读(1067)  评论(0编辑  收藏  举报