最近看到知乎上面有人讨论怎么卡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]);
}

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





