SPFA 的优化

众所周知,SPFA作为一种暴力单源最短路算法,卡掉它并不是什么难事,毕竟,毕竟。

如果想卡 SPFA 见:「笔记」如何优雅地卡 Spfa - Luckyblock

但是我们可以优化……

但是优化也可以卡

SLF 优化

\(Small \ Label \ First\) 优化(小标签优先)。用双端队列 \(deque\) 实现,常用。

就是更新完之后比较其 \(dis\) 值与 \(dis[q.front()]\) 的大小,如果较小则放前面,反之放后面。

原理就是 \(dis\) 值小的更容易更新其他节点的 \(dis\) ,使队列后面的节点尽量不满足 \(dis[v]>dis[u]+w\),这样就避免了一部分的重复入队。

复杂度 \(\mathcal{O(\text{玄学})}\)

本来是不准备写代码的,但是有人要求那我就放上别人的代码吧(((((

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<deque>
#define MAXN 10010
#define MAXM 500010
#define MAX 2147483647
using namespace std;
int n,m,s,t,c=1;
int head[MAXN],path[MAXN];
bool vis[MAXN];
struct node{
    int next,to,w;
}a[MAXM<<1];
inline int read(){
    int date=0,w=1;char c=0;
    while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    return date*w;
}
inline int relax(int u,int v,int w){
    if(path[v]>path[u]+w){
        path[v]=path[u]+w;
        return 1;
    }
    return 0;
}
inline void add(int u,int v,int w){
    a[c].to=v;a[c].w=w;a[c].next=head[u];head[u]=c++;
}
void spfa(){
    int u,v;
    deque<int> q;
    for(int i=1;i<=n;i++){path[i]=MAX;vis[i]=false;}
    path[s]=0;
    vis[s]=true;
    q.push_back(s);
    while(!q.empty()){
        u=q.front();
        q.pop_front();
        vis[u]=false;
        for(int i=head[u];i;i=a[i].next){
            v=a[i].to;
            if(relax(u,v,a[i].w)&&!vis[v]){
                vis[v]=true;
                if(!q.empty()&&path[v]<path[q.front()])q.push_front(v);
                else q.push_back(v);
            }
        }
    }
    for(int i=1;i<=n;i++)printf("%d ",path[i]);
    printf("\n");
}
int main(){
    int u,v,w;
    n=read();m=read();s=read();
    for(int i=1;i<=m;i++){
        u=read();v=read();w=read();
        add(u,v,w);
    }
    spfa();
    return 0;
}

LLL 优化

\(Large\ Label\ Last\) 优化(大标签最后)。

设队首元素为 \(temp\) ,每次松弛时进行判断,队列中所有 \(dis\) 值的和为 \(sum\),队列元素个数为 \(num\)

\(dis[temp] \cdot num > sum\) ,则将 \(temp\) 取出插入到队尾,查找下一元素,直到找到某一个 \(temp\) 使得 \(dis[ temp ] \cdot sum \le x\) ,则将 \(temp\) 出队进行松弛操作。

和 SLF 优化原理差不多,因为比平均值小的 \(dis\) 更容易更新别的 \(dis\) 从而减少入队次数。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<list>
#define MAXN 10010
#define MAXM 500010
#define MAX 2147483647
using namespace std;
int n,m,s,t,c=1;
int head[MAXN],path[MAXN];
bool vis[MAXN];
struct node{
    int next,to,w;
}a[MAXM<<1];
inline int read(){
    int date=0,w=1;char c=0;
    while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    return date*w;
}
inline int relax(int u,int v,int w){
    if(path[v]>path[u]+w){
        path[v]=path[u]+w;
        return 1;
    }
    return 0;
}
inline void add(int u,int v,int w){
    a[c].to=v;a[c].w=w;a[c].next=head[u];head[u]=c++;
}
void spfa(){
    int u,v,num=0;
    long long x=0;
    list<int> q;
    for(int i=1;i<=n;i++){path[i]=MAX;vis[i]=false;}
    path[s]=0;
    vis[s]=true;
    q.push_back(s);
    num++;
    while(!q.empty()){
        u=q.front();
        q.pop_front();
        num--;x-=path[u];
        while(num&&path[u]>x/num){
            q.push_back(u);
            u=q.front();
            q.pop_front();
        }
        vis[u]=false;
        for(int i=head[u];i;i=a[i].next){
            v=a[i].to;
            if(relax(u,v,a[i].w)&&!vis[v]){
                vis[v]=true;
                if(!q.empty()&&path[v]<path[q.front()])q.push_front(v);
                else q.push_back(v);
                num++;x+=path[v];
            }
        }
    }
    for(int i=1;i<=n;i++)printf("%d ",path[i]);
    printf("\n");
}
int main(){
    int u,v,w;
    n=read();m=read();s=read();
    for(int i=1;i<=m;i++){
        u=read();v=read();w=read();
        add(u,v,w);
    }
    spfa();
    return 0;
}

SLF + LLL 优化

因为这俩玩意互不影响,一个判出队,一个判入队,结合起来一样用。

DFS优化

这玩意可以判环,因为依靠队列判环至少需要一个点重复进队 \(n\) 次,所以极易 GG。

DFS 优化可以凭借一个 \(vis\) 数组将判环的复杂度降到到 \(\mathcal{O(\text{E})}\)

蛋是,要是没环,见 LB 如何卡 SPFA 。

复杂度指数级。


bool dfs_SPFA(int u){
    vis[u]=true;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].v;
        if(dis[v]<dis[u]+e[i].w){
            dis[v]=dis[u]+e[i].w;
            if(vis[v]) return true;
            else if(dfs_SPFA(v)) return true;
        }
    }
    vis[u]=false;
    return false;
}
posted @ 2022-04-04 10:25  Gym_nastics  阅读(151)  评论(6编辑  收藏  举报