SPFA算法详解及模板

写在前面

我们现在熟悉的Dijkstra算法基本思路是:每次寻找已知节点到达未知节点距离最短的一个节点,那么这个位置节点的最短距离我们就知道了,这个未知节点变为已知节点,再去更新未知节点的距离......那么这样我们就需要寻找N-1次就可以找到最终的答案,因为每次我们都会得到一个点的最短距离,当然,Dijkstra算法因为每次都要去找距离已知点中距离最短的点,那么我们就可以使用优先队列来进行优化,这样我们就不用再去遍历寻找了

Bellman-Ford算法相对于Dijkstra算法,他不仅可以得到单源最短路的长度,并且我们还可以判断有没有负环产生,那么我们这里的SPFA算法就是对Bellman-Ford算法的队列优化

算法解释

在SPFA算法中,我们已知一个起点,以及点之间的距离,我们还需要定一个队列,在队列中存放后面可以用来进行松弛操作的点,数组mark表示某个点进入队列的次数

从开始节点遍历边,当遍历到的点不在队列中的时候,将点放入队列,mark对应的值++,并且更新最短距离,当某一个节点进入队列次数大于n的时候,我们就可以判定图中存在负环

模板

POJ 3259

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <algorithm>
#define INF 0x3f3f3f3f

using namespace std;
const int MAXN = 5500;
int n,m,w;

struct Edge
{
    int v,w,next;
}edge[MAXN];
int head[MAXN],dis[MAXN],vis[MAXN],t;

void Init()
{
    memset(head,-1,sizeof(head));
    t = 0;
}
void Add_edge(int u,int v,int w)
{
    edge[t].v = v;edge[t].w = w;edge[t].next = head[u];head[u] = t++;
}

bool SPFA()
{
    int mark[MAXN];//记录每个点如队列的次数
    for(int i = 1;i <= n;i ++)
    {
        mark[i] = 0;dis[i] = INF;vis[i] = 0;
    }
    queue<int> q;
    q.push(1);  //我们只需要判断负环,随便找一个起点就好
    dis[1] = 0;
    vis[1] = 1;//入队列
    mark[1] ++;
    while(!q.empty())
    {
        int  u = q.front();
        q.pop();
        vis[u] = 0;//出队列
        for(int i = head[u];i != -1;i = edge[i].next)
        {
            int v = edge[i].v;
            if(dis[v] > dis[u] + edge[i].w)
            {
                dis[v] = dis[u] + edge[i].w;
                if(!vis[v])//不在队列中的时候出队
                {
                    q.push(v);mark[v] ++;vis[v] = 1;
                }
                if(mark[v] >= n)//如果不存在负环,那么最多更新n-1次就可以得到最终的答案,因为一次最少更新一个节点,那么如果出现了更新n次,那么就一定出现了负环
                    return false;
            }
        }
    }
    return true;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        Init();
        int u,v,z;
        scanf("%d%d%d",&n,&m,&w);
        for(int i = 0;i < m;i ++)
        {
            scanf("%d%d%d",&u,&v,&z);
            Add_edge(u,v,z);
            Add_edge(v,u,z);
        }
        for(int i = 0;i < w;i ++)
        {
            scanf("%d%d%d",&u,&v,&z);
            Add_edge(u,v,-z);
        }
        if(!SPFA())
            printf("YES\n");
        else
            printf("NO\n");
    }
    return 0;
}

参考博客

https://blog.csdn.net/qq_35644234/article/details/61614581

猜你喜欢

转载自blog.csdn.net/li1615882553/article/details/80373854