【图论】SPFA

关于 SPFA,它死了

算法-SPFA


前置知识


思路

我们发现,BF每次松弛只会使得它附近的节点可以松弛,我们可以采用队列,用类似于BFS的方式去维护,每次松弛后就尝试将邻近的节点加入队列中。

SPFA判负环类似于BF,计数一下即可。


极端情况

接下来我们讲解如何卡SPFA
我们发现:SPFA算法的漏洞在于它的贪心策略,即每次更新所有出边,这种策略容易被卡死。
常见的卡SPFA的方法包括:

  1. 随机网格图,走错一次路可能导致很高的额外开销。
  2. 构造链套菊花,使得队列更新菊花的次数非常高。
  3. 特殊的图,使得元素刚刚被取出,就被下一个元素入队,从而卡住总共的入队次数。
  4. 网格套菊花。。。

由于OI界的内卷进步,SPFA基本上都是以 O ( ∣ V ∣ ∣ E ∣ ) O(|V||E|) O(V∣∣E) 计算的了。


算法参数

  • 平均时间复杂度: O ( k ∣ E ∣ ) O(k|E|) O(kE)
  • 最坏时间复杂度: 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大抵是死了吧!

  • 16
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值