spfa算法及判负环详解

spfa     (Shortest Path Faster Algorithm)

是一种单源最短路径的算法,基于Bellman-Ford算法上由队列优化实现。

什么是Bellman_Ford,百度内食用QWQ

也就是说,Bellman_Ford是一种无脑,疯狂松弛的算法。其复杂度为O(nm),可想而知,对于上万的数据会炸的一塌糊涂。。。

相对而言,SPFA显得就没那么无脑了。

在Bellman_Ford算法上,我们找到了一种优化松弛的方法:对于其子边没有进行松弛的松弛操作,当前操作不可能得出正确结果,只能需要后期子边全部被松弛为最佳结果后再进行松弛得到当前边的最佳结果。

那么,在理解这个后,我们搞一个队列来优化Bellman_Ford。具体的,对于已经被松弛过的点,我们将其入队去更新其他的点,这样相对于原来用没有被优化更新的点去更新其他点来说更优。在当前点优化完其他点后,将其出队,以后也可以进队,用来优化其他边。如此循环往复直到队列为空(没有可以优化的点了),那么求出来的就是最短路。

代码,来源于单源最短路径弱化版

// luogu-judger-enable-o2
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#define spfa zhx_ak_ioi

using namespace std;

const long long inf=2147483647;

long long n,m,s;
long long dis[10008],vis[10001],head[10001],num_edge=0;

struct Edge{
    long long next,to,dis;
}edge[500008]; 

queue <long long> q;

void addedge(long long from,long long to,long long dis)
{
    num_edge++;
    edge[num_edge].next=head[from];
    edge[num_edge].to=to;
    edge[num_edge].dis=dis;
    head[from]=num_edge; 
}

void spfa()
{
    for(long long i=1;i<=n;++i)
    {
        dis[i]=inf;
        vis[i]=0;
    }
    dis[s]=0;
    vis[s]=1;
    q.push(s);
    while(!q.empty())
    {
        long long u=q.front();
        q.pop();
        vis[u]=0;
        for(long long i=head[u];i;i=edge[i].next)
        {
            long long zhongdian=edge[i].to;
            if(dis[zhongdian]>dis[u]+edge[i].dis)
            {
                dis[zhongdian]=dis[u]+edge[i].dis;
                if(!vis[zhongdian])
                {
                    q.push(zhongdian);
                    vis[zhongdian]=1;
                }
            }
        }
    }
}

int main()
{
    scanf("%lld %lld %lld",&n,&m,&s);
    for(long long i=1;i<=m;++i)
    {
        long long u,v,w;
        scanf("%lld %lld %lld",&u,&v,&w);
        addedge(u,v,w);
    }
    spfa();
    for(long long i=1;i<=n;++i)
    {
        if(i==s) printf("0 ");
        else printf("%lld ",dis[i]);
    }
    return 0;
}

判断负环:

spfa和Bellman_Ford算法可以用来判断负环。

负环,就是图上一个边权权值和为负数的环,

BFS广搜判断方法

对于一个不存在负环的图,从起点到任意一个点最短距离经过的点最多只有n个。那么定义一个cnt数组,表示当前点从起点(编号设为1)到点i的最短距离包含点的个数。如果一个cnt的值大于n的话,就可以判断负环了。同样的,对于每一次松弛操作,一旦松弛成功,我们就将下一个点的cnt值+1,对于环,我们每次松弛时直接判断cnt值是不是大于n,如果大于,说明找到了负环,那么我们就可以退出直接输出就行。

但是,在找负环时,广搜的性质决定了它的复杂度上并不适合找负环。原因:负环本质上是一条环形链(可以这么想但不一定严谨。),但是BFS考虑面太广了以至于在判环的操作上会考虑其他的不属于这个环的元素,因此搜完整个环的效率会变慢。那么有什么算法能够在遍历到这条链?的时候能够不分心的一口气走到底呢?DFS。

代码就是在原spfa上加入cnt和判断操作。

DFS深搜判断负环方法

与BFS唯一不同就是把队列换成了栈,取刚更新的点一口气继续更新到底。这样就可以一直在环上跑,几圈下来就可以判断并退出了。。

代码就是把BFS代码上换个数据结构。


你问我为什么这个代码中没有出现有关边的负权的判断?

在这上面

如果是负数的话那个条件就可以无限满足并(转圈圈跑)了。

完结。QWQ

原文地址:https://www.cnblogs.com/lbssxz/p/11223978.html