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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM