日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

最短路径之spfa

發布時間:2025/5/22 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 最短路径之spfa 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

引入1:單源最短路

問:求帶權有向圖上一個源點到其他點的最短路徑距離

如果沒有非負邊權,我們自然可以想到dij。但是如果有負邊權呢?這時候就要用SPFA算法求解。

原理&講解

用dis數組記錄源點到有向圖上任意一點距離,其中源點到自身距離為0,到其他點距離為INF。將源點入隊,并重復以下步驟:

  • 隊首x出隊
  • 遍歷所有以隊首為起點的有向邊(x,i),若dis[x]+w(x,i)<dis[i],則更新dis[i]
  • 如果點i不在隊列中,則i入隊
  • 若隊列為空,跳出循環;否則執行1

實際上我們可以將其理解為bfs

如果圖是隨機生成的,時間復雜度為 O(KM) (K可以認為是個常數,m為邊數,n為點數)

但是實際上SPFA的算法復雜度是 O(NM) ,可以構造出卡SPFA的數據,讓SPFA超時。

在NOI 2018的第一天第一題中,出題人卡了SPFA算法,導致100分變成60分,所以在沒有負環、單純求最短路徑,不建議使用SPFA算法,而是用Dijkstra算法。

在初一我們學到一條三角形中的性質,即同一三角形內兩邊之和大于第三邊。而最短路中如u->v的最短路它是小于等于其它任意路徑的,這使我們容易yy到三角形。也就是說,我們實際上每次都是在判斷這條路徑符不符合三角形不等式,若不符合,我們就將原先的路徑松弛為現在的路徑,使得現在的路徑滿足三角形不等式。但是為什么松弛后要將終點入隊呢?SPFA的過程是BFS,它是不停擴展節點的。而當我們更新了這一條路徑,那么可能會出現基于這一條路徑的新路,我們需要判斷原路與新路是否滿足三角形不等式。

模擬&代碼

我們可以手推這張圖模擬一下~

我們以1為源點,初始化:dis[源點]=0,其他為正無窮,并將源點入隊。

隊首1出隊,并枚舉它的出邊1->2,1->3。由dis[1]+w(1,2)=1<dis[2]=INF,dis[1]+w(1,3)=6<dis[3]=INF得dis[2]=dis[1]+w(1,2)=1,dis[3]=dis[1]+w(1,3)=6,并將2,3入隊。

隊首2出隊,枚舉它的出邊2->3,2->4,2->5。都不滿足三角形不等式,所以松弛它們。并將3,4,5入隊,但由于3已在隊內,所以不管。

隊首3出隊,沒有能松弛的邊,直接略過。

此時隊內剩下4,5,由于這兩點沒有出邊,所以在此不枚舉。

手繪勿噴

下面是帶注釋代碼:

#include<iostream> #include<vector> #include<algorithm> #include<cstring> #include<string> #include<cstdio> #include<cstdlib> #include<queue> #define N 110000 #define INF 0x3f3f3f3f using namespace std;int n,m,a,b,c,vis[N],dis[N];struct node {int d,w; };//定義一個結構體來存儲每個入度點以及對應邊的權值 //比如邊u->v,權值為w,node結構體存儲的就是v以及w。vector<node>v[N];void spfa(int u);int main() {//對于N非常大但是M很小的這種稀疏圖來說,用鄰接矩陣N*N是存不下的。鄰接矩陣是將所有的點都存儲下來了,然而//對于稀疏圖來說,有很多點是沒有用到的,把這些點也存儲下來的話就會很浪費空間。可以用鄰接表來存儲,這里借助vector來實現鄰接表的操作。//用鄰接表存儲時候,只存儲有用的點,對于沒有用的點不存儲,實現空間的優化。cin>>n>>m;for(int i=0; i<=n; i++)v[i].clear();//將vecort數組清空for(int i=1; i<=m; i++) //用vector存儲鄰接表{node nd;scanf("%d%d%d",&a,&b,&c);nd.d=b,nd.w=c;//將入度的點和權值賦值給結構體v[a].push_back(nd);//將每一個從a出發能直接到達的點都壓到下標為a的vector數組中,以后遍歷從a能到達的點就可以直接遍歷v[a]// nd.d=a,nd.w=c;//無向圖的雙向存邊// v[b].push_back(nd);}spfa(1);if(dis[n]!=INF)printf("%d\n",dis[n]);elseprintf("impossible");return 0; } void spfa(int u){memset(vis,1,sizeof(vis));memset(dis,0x3f,sizeof(dis));dis[u]=0;queue<int> q;q.push(u);vis[u]=false;while (!q.empty()) {int x=q.front();q.pop();vis[x]=true;vector<node> s=v[x];for (int i = 0; i < s.size(); ++i) {int v=s[i].d;if(dis[x]+s[i].w<dis[v]){dis[v]=dis[x]+s[i].w;if(vis[v]){q.push(v);vis[v]=false;}}}} }

引入2:判正(負)環

spfa算法還可以在有向圖內判正環負環,我們可以使用DFS/BFS版SPFA。注意,判負環跑最短路,判正環跑最長路。

#include<iostream> #include<vector> #include<algorithm> #include<cstring> #include<string> #include<cstdio> #include<cstdlib> #include<queue> #define N 110000 #define INF 0x3f3f3f3f using namespace std;int n,m,a,b,c,instack[N],dis[N],flag;struct node {int d,w; };//定義一個結構體來存儲每個入度點以及對應邊的權值 //比如邊u->v,權值為w,node結構體存儲的就是v以及w。vector<node>v[N];void spfa(int u);int main() {//對于N非常大但是M很小的這種稀疏圖來說,用鄰接矩陣N*N是存不下的。鄰接矩陣是將所有的點都存儲下來了,然而//對于稀疏圖來說,有很多點是沒有用到的,把這些點也存儲下來的話就會很浪費空間。可以用鄰接表來存儲,這里借助vector來實現鄰接表的操作。//用鄰接表存儲時候,只存儲有用的點,對于沒有用的點不存儲,實現空間的優化。cin>>n>>m;for(int i=0; i<=n; i++)v[i].clear();//將vecort數組清空for(int i=1; i<=m; i++) //用vector存儲鄰接表{node nd;scanf("%d%d%d",&a,&b,&c);nd.d=b,nd.w=c;//將入度的點和權值賦值給結構體v[a].push_back(nd);//將每一個從a出發能直接到達的點都壓到下標為a的vector數組中,以后遍歷從a能到達的點就可以直接遍歷v[a]// nd.d=a,nd.w=c;//無向圖的雙向存邊// v[b].push_back(nd);}memset(instack,0,sizeof(instack));memset(dis,0,sizeof(dis));flag=0;for(int i=1;i<=n;i++){spfa(i);if(flag)break;}if(flag)printf("Yes");else printf("No");return 0; } void spfa(int u){if(instack[u]){flag=1;return;}instack[u]=true;vector<node> s=v[u];for (int i = 0; i < s.size(); ++i) {if(dis[u]+s[i].w<dis[s[i].d]){dis[s[i].d]=dis[u]+s[i].w;spfa(s[i].d);if(flag)return;}}instack[u]=false; }

  

?

轉載于:https://www.cnblogs.com/clarencezzh/p/10382939.html

《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀

總結

以上是生活随笔為你收集整理的最短路径之spfa的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。