【ACM】spfa算法(适用于存在负权)

本文介绍了一种用于求解单源最短路径问题的算法——SPFA算法,它能够处理含有负权边的情况。文章详细解释了算法的基本步骤,包括初始化、松弛操作以及如何通过队列实现这些操作。此外,还介绍了两种优化策略:SLF策略和LLL策略。

一:算法描述
求单源最短路的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,则出队进行松弛操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值