演算法之最短路
阿新 • • 發佈:2018-10-31
最短路
我跟你講SPFA已經死了好吧,SPFA+堆優又太難打,那就用dijkstra吧。(負權?我管它呢)
不加任何優化的裸dijkstra
一般般快吧,N^2,N=10000時可以卡過,很好打。
#include <bits/stdc++.h> #define MAXN 1005 using namespace std; long long LIS[MAXN][MAXN];//LIS陣列儲存圖 long long dis[MAXN];//dis陣列,儲存最短長度值 long long vis[MAXN];//vis[i]代表這個點有沒有被當做源點去搜索過,1為有,0為沒有。這樣就不會重複搜尋了。 long long N,M,S; void dijkstra(long long x)//主函式,引數是源點編號 { long long start=x;//先從源點搜尋 for(long long i=1;i<=N;i++) dis[i]=LIS[x][i];//先更新一遍 dis[start]=0; vis[start]=1;//標記源點已經搜尋過 for(long long i=1;i<=N-1;i++) { long long MINN=2147483647;//比較 for(long long j=1;j<=N;j++) if(vis[j]==0&&MINN>dis[j]) MINN=dis[j],start=j;//找到離源點最近的點,然後把編號記錄下來,用於搜尋,可優化 vis[start]=1; for(long long j=1;j<=N;j++) dis[j]=min(dis[j],dis[start]+LIS[start][j]);//以新的點來更新dis。 } } int main() { cin>>N>>M>>S;//N是點數,M是邊數,S是出發點 for (long long i=1;i<=N;i++)for (long long j=1;j<=N;j++)LIS[i][j]=2147483647; for (int i=1;i<=N;i++) LIS[i][i]=0; for(long long i=1;i<=M;i++) { long long a,b,c; cin>>a>>b>>c; LIS[a][b]=min(LIS[a][b],c);//判重 //LIS[b][a]=min(LIS[a][b],c);//這是雙向邊 } dijkstra(S);//以S為源點。 for(long long i=1;i<=N;i++) cout<<dis[i]<<" "; return 0; }
堆優化+前向星儲存dijkstra
因為普通dijkstra需要找最小值然後更新,所以對於
for(long long j=1;j<=N;j++) if(vis[j]==0&&MINN>dis[j]) MINN=dis[j],start=j;
我們可以考慮用堆優化此過程,並且用鏈來儲存,極大優化了記憶體
#include<bits/stdc++.h> #define MAXN 100010 #define MAXM 200010 using namespace std; long long last[MAXN],to[MAXM],nex[MAXM],v[MAXM],cnt=0,dis[MAXN]; bool vis[MAXN]; long long n,m,s; void add(int x,int y,int w) { nex[++cnt]=last[x];//上一條邊的編號 to[cnt]=y;//此邊到哪個點 v[cnt]=w;//權值 last[x]=cnt;//以x開頭的最後一條邊的編號 } priority_queue< pair<int,int> > q; void dijkstra(int s) { for(int i=1;i<=n;i++) dis[i]=2147483647;//其實這樣也可以,因為會自己更新的 dis[s]=0; q.push(make_pair(0,s));//放入初始節點 while(!q.empty())//還未遍歷完 { int x=q.top().second;//堆頭節點,最小節點編號 q.pop();//用過彈出 if(vis[x]) continue;//已拓展的就不要再做了 vis[x]=1; for(int i=last[x];i;i=nex[i])//拓展 { int y=to[i];//下一個點 if(dis[y]>dis[x]+v[i]) { dis[y]=dis[x]+v[i]; q.push(make_pair(-dis[y],y));//用相反數實現小根堆,放入堆 } } } } int main() { cin>>n>>m>>s; for(int i=1;i<=m;i++) { int x,y,z; cin>>x>>y>>z; add(x,y,z);//單向邊 } dijkstra(s); for (int i=1;i<=n;i++) cout<<dis[i]<<" "; return 0; }