求最短路徑-----迪傑斯特拉演算法

未來首富發表於2020-12-13

3),迪傑斯特拉演算法(解決單源最短路徑)
基本思想:每次找到離源點(如1號結點)最近的一個頂點,然後以該頂點為中心進行擴充套件,最終得到源點到其餘所有點的最短路徑。
基本步驟:1,設定標記陣列book[]:將所有的頂點分為兩部分,已知最短路徑的頂點集合P和未知最短路徑的頂點集合Q,很顯然最開始集合P只有源點一個頂點。book[i]為1表示在集合P中;
2,設定最短路徑陣列dst[]並不斷更新:初始狀態下,令dst[i] = edge[s]i,很顯然此時dst[s]=0,book[s]=1。此時,在集合Q中可選擇一個離源點s最近的頂點u加入到P中。並依據以u為新的中心點,對每一條邊進行鬆弛操作(鬆弛是指由結點s–>j的途中可以經過點u,並令dst[j]=min{dst[j], dst[u]+edge[u][j]}),並令book[u]=1;
3,在集合Q中再次選擇一個離源點s最近的頂點v加入到P中。並依據v為新的中心點,對每一條邊進行鬆弛操作(即dst[j]=min{dst[j], dst[v]+edge[v][j]}),並令book[v]=1;
4,重複3,直至集合Q為空。
在這裡插入圖片描述
例1:給你n個點,m條無向邊,每條邊都有長度d和花費p,給你起點s,終點t,要求輸出起點到終點的最短距離及其花費,如果最短距離有多條路線,則輸出花費最少的。
輸入:輸入n,m,點的編號是1~n,然後是m行,每行4個數 a,b,d,p,表示a和b之間有一條邊,且其長度為d,花費為p。最後一行是兩個數s,t;起點s,終點 t。n和m為 0 時輸入結束。(1<n<=1000, 0<m<100000, s != t)
輸出:輸出一行,有兩個數, 最短距離及其花費。
分析:由於每條邊有長度d和花費p,最好構建邊結構體存放,此外可以使用鄰接連結串列,使用鄰接連結串列時需要將上面的核心程式碼修改幾個地方:
1,初始化dst[]時使用結點1的鄰接連結串列;
2,更新最短路徑陣列時,k的範圍由1n變為1edge[u].size()。先採用鄰接矩陣解決此題,再使用鄰接表解決此題,兩種方法的思路都一樣:初始化鄰接矩陣或鄰接連結串列,並
初始化最短路徑陣列dst ----> n-1輪邊的鬆弛中,先找到離新源點最近的中心點u,之後根據中心點u為轉折點來更新路徑陣列。

使用鄰接矩陣求解:

//***對於無向圖,輸入n,m,點的編號是1~n,然後是m行,每行4個數 a,b,d,p,表示a和b之間有一條邊,且其長度為d,花費為p。最後一行是兩個數s,t;起點s,終點 t。***/
//***n和m為 0 時輸入結束。(1<n<=1000, 0<m<100000, s != t)     輸出:輸出一行,有兩個數, 最短距離及其花費。***/
#include<bits/stdc++.h>
using namespace std;
#define nmax 1001
#define inf 99999999
struct Edge{
	int len;//邊長
	int cost;//權重
};
Edge edge[nmax][nmax];//鄰接矩陣
int dst[nmax];//最短路徑陣列
int spend[nmax];//最小花費陣列
int book[nmax];//標記走過的陣列
int n, m, stNode, enNode;
int main(){
	while(cin >> n >> m && n != 0 && m != 0){
		int a, b, i, j;
		//構建鄰接矩陣和最短路徑陣列
		//初始化
		for(i = 1; i <= n; i++){
			for(j = 1; j <= n; j++){
				edge[i][j].cost = 0;
				edge[i][j].len = inf;
			}
			edge[i][i].len = 0;
		}
		while(m--){//初始化
			cin >> a >> b;
			cin >> edge[a][b].len >> edge[a][b].cost;
			edge[b][a].len = edge[a][b].len;
			edge[b][a].cost = edge[a][b].cost;
		}

		cin >> stNode >> enNode;//開始的點和結束的點

		//初始化陣列
		for(i = 1; i <= n; i++){
			dst[i] = edge[stNode][i].len;
			spend[i] = edge[stNode][i].cost;
		}

		memset(book, 0, sizeof(book));
		book[stNode] = 1;

		//開始迪傑斯特拉演算法,進行剩餘n-1次鬆弛
		int k;
		for(k = 1; k <= n-1; k++){
			//找離源點最近的頂點u
			int minNode, min = inf;
			for(i = 1; i <= n; i++){
				if(book[i] == 0 && min > dst[i] /* || min == dst[i]&& edge[stNode][min].cost > edge[stNode][i].cost*/){
					min = dst[i];
					minNode = i;
				}
			}

			book[minNode] = 1;//易錯點1,錯寫成book[i]=1
			//以中心點u為轉折點來更新路徑陣列和花費陣列

			for(i = 1; i <= n; i++){
				if(book[i] == 0 && dst[i] > dst[minNode] + edge[minNode][i].len || dst[i] == dst[minNode] + edge[minNode][i].len && spend[i] > spend[minNode] + edge[minNode][i].cost){
					dst[i] = dst[minNode] + edge[minNode][i].len;//易錯點2,錯寫成dst[i]+
					spend[i] = spend[minNode] + edge[minNode][i].cost;
				}
			}
		}
		cout << dst[enNode] << setw(3) << spend[enNode] << endl;
	}
	return 0;
}


轉載
在這裡插入圖片描述

相關文章