SPFA的两个优化

本文介绍了如何优化SPFA算法以解决最短路问题。优化包括:1) 如果队头元素大于队尾元素,则交换它们的位置;2) 设定阈值t,当节点执行松弛操作的次数超过阈值时,将其重新入队并增加阈值,但不执行松弛操作。这两种优化帮助算法通过了原本难以解决的数据。


最近看到知乎上面有人讨论怎么卡spfa并给出了这道题作为实战地点
于是我就搞了两个优化把出题人精心构造的数据过了

优化1:
这个优化不是我想出来的,如果没错应该是Menci的做法
本来这个优化已经可以过掉原来的五个数据的,让后fstqwq(出题人)搞了一个新数据把这个优化卡T了
其实就是每次入队,如果队头比队尾大那么就交换(感觉很像SLF但是比SLF强)
让后为了Ac我就加了第二个优化

优化2:(原创)
首先设置一个阈值t,初始化为1
我们对每个节点x记录x所执行过的松弛操作的次数 cx c x
每次从队头取出元素x时,若 cx>t c x > t 那么把x丢入队尾,同时增大阈值t,但不执行任何其他操作
加上这个优化就可以轻松AC
这里有一份实现代码,很简洁(但是仍然没有dijk简洁)

#include<queue> 
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 100010
using namespace std;
inline int p(int& x){ ++x; return x&=131071; }
inline bool gmin(int& x,int y){
    return x>y?(x=y)|1:0; 
}
struct edge{ int v,c,nt; } G[N<<2];
int n,m,s,cnt,d[N],h[N],in[N],c[N],q[1<<17];
int main(){
    scanf("%d%d%d",&n,&m,&s);
    for(int i=1;i<=n;++i) d[i]=1<<30;
    for(int x,y,c;m--;){
        scanf("%d%d%d",&x,&y,&c);
        G[++cnt]=(edge){y,c,h[x]}; h[x]=cnt;
    }
    d[s]=0; int l=0,r=0; q[++r]=s;
    for(int x,t=1,j;l!=r;){
        x=q[p(l)];
        if(c[x]>t){ q[p(r)]=x; t+=20; continue; }
        in[x]=0; j=l+1&131071;
        for(int v,i=h[x];i;i=G[i].nt)
            if(gmin(d[v=G[i].v],d[x]+G[i].c)){
                if(!in[v]){
                    ++c[x]; q[p(r)]=v; in[v]=1;
                }
                if(d[q[j]]>d[q[r]]) swap(q[j],q[r]);
            }
    }
    for(int i=1;i<=n;++i) printf("%d ",d[i]);
}
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值