zoukankan      html  css  js  c++  java
  • 关于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]);
    }
  • 相关阅读:
    如何通过命令行窗口查看sqlite数据库文件
    eclipse自动补全的设置
    文本装饰
    注释和特殊符号
    文本装饰
    网页背景
    通过ArcGIS Server admin 查看和删除已注册的 Web Adaptor
    通过 ArcGIS Server Manager 查看已安装的 Web Adaptor
    通过 ArcGIS Server Manager 验证 DataStore
    Windows上安装ArcGIS Enterprise——以 Windows Server 2012 R2上安装 ArcGIS 10.8为例
  • 原文地址:https://www.cnblogs.com/bljfy/p/9262309.html
Copyright © 2011-2022 走看看