一:算法描述
求单源最短路的SPFA算法,是一种可以处理负权边的算法。对于存在负权边,迪杰斯特拉算法不能使用,但是bellman-ford时间复杂度较高。
简洁起见,我们约定有向加权图G不存在负权回路,即最短路径一定存在。当然,我们可以在执行该算法前做一次拓扑排序,以判断是否存在负权回路。
二:算法基本步骤
几乎所有的最短路径算法都是以下两个步骤:
①初始化
②松弛操作
初始化:
dis数组全部赋值为INF,vis数组标记是否在队列中,一开始队列中没有结点,所有vis数组元素都设置为false。
队列+松弛操作:
读取队头元素,出队并且修改vis数组值为false;将与点u相连的所有点v进行松弛操作,如果能更新估计值(即令d[v]变小),那么就更新,另外,如果点v没有在队列中,那么要将点v入队(记得标记),如果已经在队列中了,那么就不用入队。(因为被松弛过的结点可能会影响到其他结点的dis值,所以要继续入队)
三:判断
判断有无负环:
如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图)
四:代码
#include<cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2005;
int nodeNum , edgeNum;
int dis[N]; // d[i]表示源点s到i的距离
int c[N]; // 统计每一个结点入队的次数
bool vis[N];
const int INF = 0x3f3f3f3f;
bool loop; //判断是否是回路
int map[nodeNum][nodeNum];
void spfa(int start)
{
//建立队列,初始化dis数组和vis数组,把源结点加入到队列中
queue<int >q;
memset(vis,false,sizeof(vis));
for(int i=1;i<=nodeNum;i++)
dis[i]=INF;
dis[start]=0;
q.push(start);
vis[start]=true;
//在队列头部取点,然后修改相关数据,并对该边所连接的邻接点进行松弛操作
//判断相邻的结点vis[],是否在队列中,要是不在,加入队列
while(!q.empty())
{
int temp = q.front();
q.pop();
vis[temp] = false;
for(int i=1;i<=nodeNum;i++)
{
if(dis[temp] + map[temp][i] < dis[i])
{
dis[i] = dis[temp] + map[temp][i];
if(!vis[i])
{
q.push(i);
vis[i] = true;
}
}
}
}
}
五:优化策略
1.SLF策略:
若要加入的结点是j,队首结点是i,如果dis[j] < dis[i],则将j插入到队首;否则,插入到队尾。
2.LLL策略
设队首元素为i,队列中所有dis的平均值是x,若dis[i] > x,则将i插入到队尾,查找下一个元素知道找到一个dis[i]<=x,则出队进行松弛操作。
本文介绍了一种用于求解单源最短路径问题的算法——SPFA算法,它能够处理含有负权边的情况。文章详细解释了算法的基本步骤,包括初始化、松弛操作以及如何通过队列实现这些操作。此外,还介绍了两种优化策略:SLF策略和LLL策略。
2443

被折叠的 条评论
为什么被折叠?



