[编程笔记]-SPFA

实质

关于SPFA,它亖了

就是对Bellman-Ford算法的一种优化。事实证明优化地很成功,直接给BF差不多干没了

可以得出,节点i路程变化,当且仅当指向i的节点的路程变化,然后就能写出一个接近bfs的算法。

特性

通常情况下,SPFA是快于Dijkstra的。

!!!但是!SPFA它亖了容易被卡啊!被卡了就真亖了!!!

实现

  1. 把起点插入队列
  2. 取出队头,更新队头的所有出边。
  3. 每个更新成功的出边,都把另一个节点插入队列。
  4. 重复执行2、3步直到队列为空。

有没有发现长得很像堆优化的Dijkstra?像就对了(^▽^)

注意:不同于Dij,SPFA存储的判断数组是判断元素是否在队列中(可重复访问),而Dij的是判断元素有没有被访问过(不可重复访问)

同时,因为负环的存在,是不存在“剪枝”操作的~

判断负环

顺便记录一下路径经过的点个数,如果个数超过n,就说明有环了。这个环还能被更新,就说明它是个负环

注意,开始时所有点都是起点

代码

最短路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include<bits/stdc++.h>
using namespace std;

typedef pair<int,int> pii;

const int N=1.5e5+5;

int n,m;
int x,y,z;
int dis[N];

int idk;
int hd[N],ne[N],to[N],ed[N];

void merge(int u,int v,int x){
ed[++idk]=x;
to[idk]=v;
ne[idk]=hd[u];
hd[u]=idk;
}

void spfa(int be,int en){
memset(dis,0x3f,sizeof(dis));
bool inq[N]={false};
queue<int> q;
q.push(be);
dis[be]=0;
inq[be]=true;
while(!q.empty()){
int now=q.front();q.pop();
inq[now]=false;
//不可以加“剪枝”,因为有负环
for(int i=hd[now];i;i=ne[i]){
if(dis[to[i]]>dis[now]+ed[i]){
dis[to[i]]=dis[now]+ed[i];
if(!inq[to[i]]){
q.push(to[i]);
inq[to[i]]=true;
}
}
}
}
if(dis[en]==0x3f3f3f3f)cout<<"impossible";
else cout<<dis[en];
}

int main(){
cin>>n>>m;
while(m--){
cin>>x>>y>>z;
merge(x,y,z);
}
spfa(1,n);
return 0;
}

判断负环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include<bits/stdc++.h>
using namespace std;

typedef pair<int,int> pii;

const int N=1.5e5+5;

int n,m;
int x,y,z;
int dis[N],cnt[N];

int idk;
int hd[N],ne[N],to[N],ed[N];

void merge(int u,int v,int x){
ed[++idk]=x;
to[idk]=v;
ne[idk]=hd[u];
hd[u]=idk;
}

bool spfa(){
bool inq[N]={false};
queue<int> q;
for(int i=1;i<=n;i++){
q.push(i);
cnt[i]=1;
inq[i]=true;
}
while(!q.empty()){
int now=q.front();q.pop();
inq[now]=false;
for(int i=hd[now];i;i=ne[i]){
if(dis[to[i]]>dis[now]+ed[i]){
dis[to[i]]=dis[now]+ed[i];
cnt[to[i]]=cnt[now]+1;
if(cnt[now]>n)return true;
if(!inq[to[i]]){
q.push(to[i]);
inq[to[i]]=true;
}
}
}
}
return false;
}

int main(){
cin>>n>>m;
while(m--){
cin>>x>>y>>z;
merge(x,y,z);
}
if(spfa())cout<<"Yes";
else cout<<"No";
return 0;
}

完结撒花o( ̄︶ ̄)o


[编程笔记]-SPFA
http://githarlem.github.io/2024/08/03/SPFA/
作者
Harlem
发布于
2024年8月3日
更新于
2024年8月4日
许可协议