• 关于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]);
    }
  • 相关阅读:
    String类可以被继承吗?我们来聊聊final关键字!
    微信小程序中使用阿里ICON图标
    兼容iphone x刘海的正确姿势
    解决ios下部分手机在input设置为readonly属性时,依然显示光标
    react jsx 中使用 switch case 示例
    react 中使用 JsBarcode 显示条形码
    解决IDEA输入法输入中文候选框不显示问题
    svn提交代码失败提示清理(清理失败并且报错信息乱码解决办法)
    css笔记
    修改Mysql数据库的字符集
  • 原文地址:https://www.cnblogs.com/bljfy/p/9262309.html
Copyright © 2020-2023  润新知