关于 SPFA,它死了
算法-SPFA
前置知识
思路
我们发现,BF每次松弛只会使得它附近的节点可以松弛,我们可以采用队列,用类似于BFS的方式去维护,每次松弛后就尝试将邻近的节点加入队列中。
SPFA判负环类似于BF,计数一下即可。
极端情况
接下来我们讲解如何卡SPFA
我们发现:SPFA算法的漏洞在于它的贪心策略,即每次更新所有出边,这种策略容易被卡死。
常见的卡SPFA的方法包括:
- 随机网格图,走错一次路可能导致很高的额外开销。
- 构造链套菊花,使得队列更新菊花的次数非常高。
- 特殊的图,使得元素刚刚被取出,就被下一个元素入队,从而卡住总共的入队次数。
- 网格套菊花。。。
由于OI界的内卷进步,SPFA基本上都是以
O
(
∣
V
∣
∣
E
∣
)
O(|V||E|)
O(∣V∣∣E∣) 计算的了。
算法参数
- 平均时间复杂度: O ( k ∣ E ∣ ) O(k|E|) O(k∣E∣)
- 最坏时间复杂度: O ( ∣ V ∣ ∣ E ∣ ) O(|V||E|) O(∣V∣∣E∣)
- 空间复杂度: O ( ∣ V ∣ + ∣ E ∣ ) O(|V|+|E|) O(∣V∣+∣E∣)
实现代码
void spfa(){
memset(s,0x3f,sizeof(s));
memset(w,0,sizeof(w));
memset(cnt,0,sizeof(cnt));
s[1]=0;
w[1]=1;
Q.push(1);
while (!Q.empty()){
int u=Q.front();
Q.pop();
w[u]=0;
for (int i=0;i<g[u].size();i++){
int v=g[u][i],k=d[u][i];
if (s[v]>s[u]+k){
s[v]=s[u]+k;
cnt[v]=cnt[u]+1;
if (cnt[v]>=n) return;
if (!w[v]){
Q.push(v);
w[v]=1;
}
}
}
}
}
练习
小剧场
SPFA是写最短路径而不用堆优化的唯一的人。
他身材很高大;青白脸色,皱纹间时常夹些伤痕;
一部乱蓬蓬的花白的胡子。穿的虽然是女装,可是又脏又破,似乎十多年没有补,也没有洗。
他对人说话,总是满口O(kE),叫人半懂不懂的。
因为他姓S,别人便从描红纸上的“Shortest Path Faster Algorithm”这半懂不懂的话里,替他取下一个绰号,叫作SPFA。
SPFA一到机房,所有写代码的人便都看着他笑,有的叫道,“SPFA,你又TLE了!”
他不回答,对我说,“打1e5个结点,要2e5条边。”便排出一条队列。
他们又故意的高声嚷道,“你一定又被出题人卡了!”SPFA睁大眼睛说,“你怎么这样凭空污人清白……”
“什么清白?我前天亲眼见你被出题人卡到O(nm),吊着打。”
SPFA便涨红了脸,额上的青筋条条绽出,争辩道,“TLE不能算O(nm)……O(nm)!
卡常数的事,能算O(nm)么?”接连便是难懂的话,什么“SPFA的复杂度是O(kE)”,什么“可以证明k一般小于等于2”之类。
引得众人都哄笑起来;机房内外充满了快活的空气。
现在,我已经一年没看见也没听别人说过SPFA,SPFA大抵是死了吧!