一提起最短路,各位oier會想到什么呢?
floyd,spfa,dij,或是bellman-ford?
其實,只要學會一種算法,大部分最短路問題就能很快解決了。
他就是堆優化的dijkstra。
首先,先講一下dij是怎么求最短路的。
Dijkstra是基於一種貪心的策略,首先用數組dis記錄起點到每個結點的最短路徑,再用一個數組保存已經找到最短路徑的點
然后,從dis數組選擇最小值,則該值就是源點s到該值對應的頂點的最短路徑,並且把該點記為已經找到最短路
此時完成一個頂點,再看這個點能否到達其它點(記為v),將dis[v]的值進行更新
不斷重復上述動作,將所有的點都更新到最短路徑
這種算法實際上是O(n^2)的時間復雜度,但我們發現在dis數組中選擇最小值時,我們可以用一些數據結構來進行優化。
其實我們可以用STL里的堆來進行優化,堆相對於線段樹以及平衡樹有着常數小,碼量小等優點,並且堆的一個妙妙的性質就是可以在nlogn的時限內滿足堆頂是堆內元素的最大(小)值,之不正是我們要的嘛?
但是呢,dij處理不了負邊,所以當題目出現負邊時,dij就不能用了。
但反過來說,只要題目沒負邊,SPFA是一定會被卡的!
下面上代碼:
#include<bits/stdc++.h> using namespace std; #define maxn 10005 #define maxm 500005 #define INF 1234567890 inline int read() { int x=0,k=1; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')k=-1;c=getchar();} while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar(); return x*k; } struct Edge { int u,v,w,next; }e[maxm]; int head[maxn],cnt,n,m,s,vis[maxn],dis[maxn]; struct node { int w,now; inline bool operator <(const node &x)const //重載運算符把最小的元素放在堆頂(大根堆) { return w>x.w;//這里注意符號要為'>' } }; priority_queue<node>q; //優先隊列,其實這里一般使用一個pair,但為了方便理解所以用的結構體 inline void add(int u,int v,int w) { e[++cnt].u=u; //這句話對於此題不需要,但在縮點之類的問題還是有用的 e[cnt].v=v; e[cnt].w=w; e[cnt].next=head[u]; //存儲該點的下一條邊 head[u]=cnt; //更新目前該點的最后一條邊(就是這一條邊) } //鏈式前向星加邊 void dijkstra() { for(int i=1;i<=n;i++) { dis[i]=INF; } dis[s]=0; //賦初值 q.push((node){0,s}); while(!q.empty()) //堆為空即為所有點都更新 { node x=q.top(); q.pop(); int u=x.now; //記錄堆頂(堆內最小的邊)並將其彈出 if(vis[u]) continue; //沒有遍歷過才需要遍歷 vis[u]=1; for(int i=head[u];i;i=e[i].next) //搜索堆頂所有連邊 { int v=e[i].v; if(dis[v]>dis[u]+e[i].w) { dis[v]=dis[u]+e[i].w; //松弛操作 q.push((node){dis[v],v}); //把新遍歷到的點加入堆中 } } } } int main() { n=read(),m=read(),s=read(); for(int i=1,x,y,z;i<=m;i++) { x=read(),y=read(),z=read(); add(x,y,z); } dijkstra(); for(int i=1;i<=n;i++) { printf("%d ",dis[i]); } return 0; }
謝謝大家!