前言:

今天复习这个算法十分犹豫,大家都说spfa算法已死,即在比赛中容易被卡常。

但是大多数情况下用在存在负权边还是可以的,而且还能判断是否存在负权环,只能说该算法的优点远远大于缺点。

所以我是坚决不会用它的

概述:

1.spfa在Bellman-Ford算法的基础上,利用队列来进行时间优化的一种可用于解决负边权判断负环的最短路算法。

2.spfa实际上就是先将起始点放入队列中,每次取出一个点,利用这个点将其周围的(与其相连的)点进行优化,如果某点有过松弛,就入队,重复这样的过程直到队列为空。但需要记录进队次数,如果一个点入队次数多于n次,即说明存在负环,则返回负环的结果。

3.判断是否存在负环时,只需要加一个数组time,来表示每个点入队的次数,当大于n时,执行关于负环的操作即可。

4.spfa时间复杂度一般为O(km),k为一个常数,m为边的个数(当然可以创造出卡spfa的数据使其变为O(nm)),所以没有负权值的情况下优先考虑其他最短路算法。

常见的模板(这里用邻接表)

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
int spfa(){
queue<PII> q;
memset(dist,0x3f,sizeof dist);
dist[1]=0;//初始化源点
q.emplace(dist[1],1);//first存放目前到源点的最短距离(该距离可被更新),second存放索引
st[1]=true;
while(q.size()){
PII p=q.front();
q.pop();
int t=p.second;
st[t]=false;//从队列中取出来之后该节点st被标记为false,代表之后该节点如果发生更新可再次入队
for(int i=h[t];i!=-1;i=ne[i]){
int j=e[i];
if(dist[j]>dist[t]+w[i]){
dist[j]=dist[t]+w[i];
if(!st[j]){//当前已经加入队列的结点,无需再次加入队列,即便发生了更新也只用更新数值即可,重复添加降低效率
st[j]=true;
q.emplace(dist[j],j);
}
}
}
}
if(dist[n]==0x3f3f3f3f) return -1;
else return dist[n];
}

实践

题目:采购特价商品

中山路店山店海,成了购物狂爱与愁大神的“不归之路”。中山路上有 n(n≤100)家店,每家店的坐标均在-10000与10000之间,其中m 家店之间有通路。若有通路,则表示可以从一家店走到另一家店,通路的距离为两点间的直线距离。现在爱与愁大神要找出从一家店到另一家店之间的最短距离。你能帮爱与愁大神算出吗?

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
58
59
60
#include<bits/stdc++.h>
using namespace std;
const int N=105;
bool st[N];
double dist[N],g[N][N];
int x[N],y[N];
int n,m;
int s,e;
long long sqr(int x){
return x*x;
}
void ad(int u,int v){
long long b = (x[v]-x[u])*(x[v]-x[u])+(y[v]-y[u])*(y[v]-y[u]);
double a=sqrt(b);
g[u][v]=a;
g[v][u]=a;
}
void spfa(int s){
queue<int>q;
q.push(s);
dist[s] = 0;
st[s] = 1;
while(!q.empty())
{
int now = q.front();
q.pop();
st[now] = 0;
for(int i = 1; i <= n; ++i)
{
if(dist[i]>g[now][i]+dist[now])
{
dist[i] =g[now][i]+dist[now];
if(!st[i])
{
q.push(i);
st[i] = 1;
}
}
}
}
}

int main(){
memset(g,0x7f,sizeof g);
memset(dist,0x7f,sizeof dist);
cin>>n;
for(int i=1;i<=n;i++){
cin>>x[i]>>y[i];
}
cin>>m;
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
ad(u,v);
}
cin>>s>>e;
spfa(s);
printf("%.2f",dist[e]);
return 0;
}