最短路複習(Floyd與Dijkstra)

理論上來說,現在只會SPFA基本上就夠NOIp用了,但是還是忍不住重新複習了一下。。。


Dijkstra算法

該算法僅用於無負權邊的圖,在稠密圖(邊數m接近於n^2的圖,網格圖就是一種非常稠密的圖,相對的,稀疏圖就是m遠小於n^2的圖)中的表現比較優秀,可以用鄰接表也可以用鄰接矩陣。

算法主體


算法也不算難,核心就是鬆弛操作,樸素的Dijkstra算法的時間複雜度爲O(n^2)
①僞代碼

清除所有點的標記(flag[i]=0)
dis[s]=0,其他dis[i]=INF      //s爲起點的序號
循環n次{                      //n爲結點數
  在所有未標記的結點中選出dis最小的結點x
  標記x(flag[x]=1)
  對從x出發的所有邊V(x,y),更新dis[y]=min{dis[y],dis[x]+V(x,y).w}
}
②打印路徑
省空間費時間方案

與dp中的方案打印一樣:從重點出發,一直順着d[i]+V(i,j).w==d[j]的路徑回退到起點

省時間費空間方案

改一下鬆弛的寫法,在鬆弛的同時在另一個數組記錄下前驅結點

if(dis[x]+V(x,y).w<dis[y])
{
    pre[y]=x;
    dis[y]=dis[x]+V(x,y).w;
}

③堆優化

不難看出每次挑選點的過程佔了時間複雜度的一個階位之多,每次挑選出dis最小值,那麼我們就可以考慮維護一個小根堆來加速這個算法中選結點的過程
這個小根堆每次鬆弛完一個點的所有邊之後要更新一下,去掉標記過的點,更新結點的dis值,由此不難得出時間複雜度爲O((n+m)logn)

Floyd

動態規劃算法…深入理解挺困難的,但是代碼真的短…
時間複雜度是O(n^3),應用於多源最短路,存圖用鄰接矩陣

①初始化

#define For(i,l,r) for(int i=l;i<=r;++i)
memset(dis,INF,sizeof dis);//INF不能太大也不能太小,太大會溢出,太小就。。
For(i,1,n)
 dus[i][i]=0;

然後有向邊就是dis[from][to]=val
無向邊就是兩條有向邊

②算法主體

For(k,1,n)
 For(i,1,n)
  For(j,1,n)
   dis[i][j]=min{dis[i][j],dis[i][k]+dis[k][j]};

③判聯通

首先改一下預處理的方式

memset(dis,0,sizeof dis);
For(i,1,n)
 dis[i][i]=1;

然後邊就是dis[from][to]=1
算法部分的處理語句改爲

dis[i][j]=dis[i][j]||(dis[i][k]&&dis[k][j]);

好像叫“傳遞閉包”。。。迷。。。

DFS-SPFA

主要是用於判負環
有個比較簡單的練手題
P2850 【[USACO06DEC]蟲洞Wormholes】
我的下一篇博客應該就是這個的題解了。。。所以這裏就不說了

BFS-SPFA

是Bellman-Ford的隊列優化,鬆弛了一個點之後,繼續鬆弛這個點能到達的所有結點,一直到隊列空了
存在負環的時候不存在最短路,這個應該是顯然的
模板題
P1339 [USACO09OCT]熱浪Heat Wave
P3371 【模板】單源最短路徑

很常用的算法,複雜度是O(ke),k一般是2,e是邊數,最壞情況是O(nm)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章