算法總結—最短路徑算法

針對 優先級隊列優化過的dijkstra算法 以及 SPFA算法 進行討論

一號選手:優先級隊列優化過的dijkstra算法 (本質:bfs[ 隊列改爲優先級隊列 ] + dis[]數組記錄到達各點的距離 )

先貼出該算法的實現模板:

void dijkstra(int start) {
	for(int i=1;i<=T;i++) {
		dis[i]=inf;
	}
	dis[start]=0;
	
	priority_queue<node> pq;
	node nd;//臨時存儲變量使用 
	nd.at=start,nd.len=0;
	pq.push(nd);
	
	while(!pq.empty()) {
		node fa=pq.top(); pq.pop();	
		for(int i=0;i<mp[fa.at].size();i++) {
			node ch=mp[fa.at][i];
			if(fa.len+ch.len<dis[ch.at]) {
				dis[ch.at]=fa.len+ch.len;
				nd.at=ch.at,nd.len=fa.len+ch.len;
				pq.push(nd);
			}
		}
	}
} 

1、在沒有負權邊的情況下求某個點到其他所有點的距離

由於沒有負權邊,隊列的top返回當前路徑中最短的一條路徑的可達點,那麼到達該點的路徑一定最短,

所以相同的點不會第二次進入隊列,因爲這行代碼

fa.len+ch.len<dis[ch.at]

點評:耗時非常穩定,時間複雜度爲elogv,在情況1下推薦優先使用。

2、在沒有負權邊的情況下求某個點到一個指定點的距離

由於沒有負權邊,所以可以在目標點進入隊列的時候直接break跳出,即可以得到最短路徑。

點評:在好的情況下可以快速找到結果,最壞情況下也不會耗時超過elogv,情況2下推薦優先使用。

3、有負權邊的情況下求某個點到其他所有點的距離

由於存在負權邊,隊列的top不再是最短路徑了,因爲不知道哪裏冒出一條負權邊可能會打破原先的最短情況。

所以同一個點可能會入隊列多次,並且一定要等到隊列爲空纔可以跳出,否則dis【i】裏的答案不一定正確的。

點評:耗時高昂,在情況3下,非常不推薦使用。

4、在有負權邊的情況下求某個點到一個指定點的距離

由於存在負權邊,所以與情況3一樣,必須要等到隊列爲空纔可以跳出。

點評:非常不推薦使用,即耗時高昂又顯得弱智。

二號選手:SPFA算法 (本質:bfs[ 加一個數組判斷點是否在隊列內 ] + dis[]數組記錄到達各點的距離 )

先貼出該算法的實現模板

void spfa(int start) {
	for(int i=0;i<=T;i++) {
		dis[i]=inf,has[i]=0;
	}
	dis[start]=0;
	
	deque<int> dq;
	dq.push_back(start);
	
	while(!dq.empty()) {
		int fa=dq.front(); dq.pop_front(); has[fa]=0;
		
		for(int i=0;i<mp[fa].size();i++) {
			node ch=mp[fa][i];
			if(dis[fa]+ch.len<dis[ch.at]) {
				dis[ch.at]=dis[fa]+ch.len;
				if(!has[ch.at]) {
					has[ch.at]=1;
					dq.push_back(ch.at);	
				}
			}
		}
		
	}
}

是否有負權邊對SPFA算法不影響,直接討論存在負權邊的情況。

1、求某個點到其他所有點的最短路徑

算法效率聽說不錯,但是非常不穩定,算法證明也有問題,自行百度SPFA算法。

點評:聽說不錯,實戰也確實非常優秀,特別是在有負權邊的情況下。

2、某個點到一個指定點的距離

這個與情況1一樣,不管是否存在負權邊,一定要等到隊列爲空纔可以跳出,否則答案不一定正確。


總結:在沒有負權邊的情況下優先考慮dijkstra算法,比較穩定,當然SPFA也非常優秀。

有負權邊的情況下那就使用SPFA算法,總體來說SPFA選手勝出。

最後說明一點, 沒經過 優先級隊列優化的dijkstra算法不能處理負權邊的情況。

最最後,貼出經過 SLF優化 和 LLL優化的 SPFA算法,但是在實測中LLL優化反而增加了耗時。

測試例子是http://lx.lanqiao.cn/problem.page?gpid=T22

代碼模板如下:

void spfa(int start) {
	for(int i=0;i<=T;i++) {
		dis[i]=inf,has[i]=0;
	}
	dis[start]=0;
	
	deque<int> dq;
	dq.push_back(start);
	
	int sum=0,cnt=1; //LLL優化需要使用的兩個變量 
	
	while(!dq.empty()) {
		
		while(dis[dq.front()]*cnt>sum) {//LLL優化 
			dq.push_back(dq.front());
			dq.pop_front();
		}
		
		int fa=dq.front(); dq.pop_front();
		has[fa]=0;
		sum-=dis[fa]; cnt--;
		
		for(int i=0;i<mp[fa].size();i++) {
			node ch=mp[fa][i];
			if(dis[fa]+ch.len<dis[ch.at]) {
				dis[ch.at]=dis[fa]+ch.len;
				if(!has[ch.at]) {
					has[ch.at]=1;
					if(dis[ch.at]<dis[dq.front()]) {//SLF優化 
						dq.push_front(ch.at);
					}else {
						dq.push_back(ch.at);
					}
					sum+=dis[ch.at]; cnt++;
				}
			}
		}	
	}
}

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