SPFA 的优化

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

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

但是我们可以优化……

但是优化也可以卡

SLF 优化

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

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

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

复杂度 O(玄学)

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

#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]num>sum ,则将 temp 取出插入到队尾,查找下一元素,直到找到某一个 temp 使得 dis[temp]sumx ,则将 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 数组将判环的复杂度降到到 O(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;
}

本文作者:Gym_nastics

本文链接:https://www.cnblogs.com/BlackDan/p/16098485.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Gym_nastics  阅读(163)  评论(6编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起