SPFA 的优化
引
众所周知,SPFA作为一种暴力单源最短路算法,卡掉它并不是什么难事,毕竟,毕竟。
如果想卡 SPFA 见:「笔记」如何优雅地卡 Spfa - Luckyblock
但是我们可以优化……
但是优化也可以卡
SLF 优化
优化(小标签优先)。用双端队列 实现,常用。
就是更新完之后比较其 值与 的大小,如果较小则放前面,反之放后面。
原理就是 值小的更容易更新其他节点的 ,使队列后面的节点尽量不满足 ,这样就避免了一部分的重复入队。
复杂度 。
本来是不准备写代码的,但是有人要求那我就放上别人的代码吧(((((
#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 优化
优化(大标签最后)。
设队首元素为 ,每次松弛时进行判断,队列中所有 值的和为 ,队列元素个数为 。
若 ,则将 取出插入到队尾,查找下一元素,直到找到某一个 使得 ,则将 出队进行松弛操作。
和 SLF 优化原理差不多,因为比平均值小的 更容易更新别的 从而减少入队次数。
#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优化
这玩意可以判环,因为依靠队列判环至少需要一个点重复进队 次,所以极易 GG。
DFS 优化可以凭借一个 数组将判环的复杂度降到到
蛋是,要是没环,见 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 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!