給定一個n個頂點,m條邊的有向圖(其中某些邊權可能為負,但保證沒有負環)。請你計算從1號點到其他點的最短路(頂點從1到n編號)。
第一行兩個整數n, m。
接下來的m行,每行有三個整數u, v, l,表示u到v有一條長度為l的邊。
對於10%的數據,n = 2,m = 2。
對於30%的數據,n <= 5,m <= 10。
對於100%的數據,1 <= n <= 20000,1 <= m <= 200000,-10000 <= l <= 10000,保證從任意頂點都能到達其他所有頂點。
我在網上搜索了最短路算法,發現基本有4種。
1.floyd 算法 2.dijkstra算法 3.bellman算法 4.spfa算法
floyd算法時間復雜度高,dijkstra算法不能計算帶負邊權的圖,故均不考慮。
spfa算法是bellman的一種優化,所以決定從簡單的先開始。
bellman實現的代碼如下:
#include <iostream>#include<string.h>
using namespace std;
int dis[20005],u[200005],v[200005],w[200005];//dis[i]代表第一點到第i點距離,u[i],v[i],w[i]分別代表第i條邊的起始點,終點和長度。
int main()
{
int m,n;
cin>>n>>m;
for(int i=1;i<=m;i++)
{
cin>>u[i]>>v[i]>>w[i];
}
memset(dis,10001,sizeof(dis));
dis[1]=0;
for(int k=1;k<n;k++)//最極端的情況下,一個點要松弛n-1次
{
for(int i=1;i<=m;i++)//枚舉所有的條邊
{
if(dis[v[i]]>dis[u[i]]+w[i])
//如果第一點到v[i]點的距離大於第一點到u[i]再加上u[i]到v[i]的距離
dis[v[i]]=dis[u[i]]+w[i];//說明dis[v[i]]的值可以更小,所以更新dis[v[i]]的值,專業的說法就是進行了一次松弛操作
}
}
for(int i=2;i<=n;i++)
cout<<dis[i]<<endl;
}
SPFA(Shortest Path Faster Algorithm)算法是求單源最短路徑的一種算法,它是Bellman-ford的隊列優化,它是一種十分高效的最短路算法。
SPFA的復雜度大約是O(kE),k是每個點的平均進隊次數(一般的,k是一個常數,在稀疏圖中小於2)。
但是,SPFA算法穩定性較差,在稠密圖中SPFA算法時間復雜度會退化。
實現方法:建立一個隊列,初始時隊列里只有起始點,在建立一個表格記錄起始點到所有點的最短路徑(該表格的初始值要賦為極大值,該點到他本身的路徑賦為0)。然后執行松弛操作,用隊列里有的點去刷新起始點到所有點的最短路,如果刷新成功且被刷新點不在隊列中則把該點加入到隊列最后。重復執行直到隊列為空。
此外,SPFA算法還可以判斷圖中是否有負權環,即一個點入隊次數超過N。
#include <iostream>#include<string.h>#include<queue> using namespace std;struct edge{ int to,val,next;}e[200100];//e[i].to,e[i].val,e[i].next分別表示第i條邊的終點,權值,和另(上)一條於此邊相同起點的邊的編號int m,n,head[21000],dis[21000];//head[i]表示當前以i為起點的邊的編號,dis[i]表示1號點到i號點的距離void add(int from,int to,int val,int len)//添加邊,參數分別代表此邊的起點,終點,權值,還有此邊的編號{ e[len].to=to; e[len].val=val; e[len].next=head[from];//此邊的起點是from,而head[from]保存上一條起點是from的邊的編號 head[from]=len;//添加了這條邊后,現在,最新的以from為起點的邊編號就是len}void spfa(){ int s; queue <int> q; int visit[21000];//visit[i]==0代表第i點不在隊列中,visit[i]==1代表已在隊列中 for(int i=1;i<=n;i++) { dis[i]=99999999; }//將權值初始化為最大值 memset(visit,0,sizeof(visit));//初始沒有點在隊列中 dis[1]=0; q.push(1); visit[1]=1;//現在把第一個點扔進隊列 while(!q.empty())//如果隊列不空則重復執行以下操作 { int t=q.front();//取隊列的第一個元素 q.pop(); visit[t]=0;//t現在不在隊列里 for(int i=head[t];i!=-1;i=e[i].next)//枚舉所有以t為起點的邊 { s=e[i].to;//s為所枚舉的邊的終點 if(dis[s]>dis[t]+e[i].val)//嘗試松弛s點 { dis[s]=dis[t]+e[i].val; if(visit[s]==0) { q.push(s); visit[s]=1; }//如果成功松弛了s點,把s點扔進隊列 } } } }int main (){ cin>>n>>m; int from,val,to; int len=1; memset (head,-1,sizeof(head)); for(int i=0;i<m;i++) { cin>>from>>to>>val; add(from,to,val,len); len++; } spfa(); for(int i=2;i<=n;i++) { cout<<dis[i]<<endl; }}
本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。