『最短路徑』單源無負權值最短路徑算法——Dijkstra算法(優先隊列優化 + C++實現 + 例題)

『算法原理』


最短路徑(Shortest Path):一個結點到另一個結點的最小權值和。

Dijkstra算法同時也叫單源最短路算法,其思想是——按路徑長度遞增的次序產生最短路的算法。

通俗來講就是,找出從源點開始通過1條邊可以到達的點的最小路徑,2條邊可以到達的點的最小路徑,....,n-1條邊可以到達的點的最小路徑,將權值最小的點加入到集合S中,一直更新到終點位置,找到源點和終點的最小路徑,將所有結點都加入到S中,結束算法。和Prim算法十分類似。

算法步驟如下:

a.找到一個源點s,更新源點和其他結點的距離(權值),如果不能直接到達就先賦值爲無窮大,將源點加入到集合S。

b.更新從源點出發最多經過1條邊就可以到達的點的最小權值,將權值最小的點加入到集合S,最多2條邊就可以的點的最小權值,...,最多n-1條邊就可以的點的最小權值,將權值最小的點加入到集合S。

c.把所有的結點都加入到集合S中,找到源點和終點的最短路徑,結束算法。

【算法圖示】

  對於圖G:

 a.選擇一個源點A,更新A到B,C,D,E的距離,將A加入到集合S中

b(1).更新從源點開始通過1條邊就可以到達的點,找到其中的權值最小的點B,更新到各結點的權值,將B加入到集合S中

 b(2).更新從源點開始最多通過2條邊就可以到達的點,找到未加入到集合S的結點中的權值最小的點D,更新到各結點的權值,將D加入到集合S中

找到未加入到集合S的結點中的權值最小的點C,更新到各結點的權值,將C加入到集合S中

 b(3).更新從源點開始最多通過3條邊就可以到達的點, 找到未加入到集合S的結點中的權值最小的點E,更新到各結點的權值,將E加入到集合S中

算法結束。

『算法模板』

 

【樸素模板】

#include <iostream>
#include <cstring>
using namespace std;
const int N = 107,INF= 0x3f3f3f3f;
int map[N][N],n,m;

void dijstra(){
	int i,j,min,u;
	int d[N],vis[N];
	//d數組表示從原點到i點的最短距離
	//vis用於表達這個點是否已經被選中
	for(i=1;i<=n;i++){
		d[i]=map[1][i];
		vis[i]=0;
	}
	vis[1] = 1;
	for(i=1;i<=n-1;i++){
		min=INF;
		for(j=1;j<=n;j++)		//每次找點的過程,首先這個點沒有被發現,然後找一個最小點
			if(vis[j]==0 && d[j]<min){
				min=d[j];//記錄最小值 
				u=j;
			}
		vis[u] = 1;
		for(int v = 1; v <= n; v++)
			if(map[u][v]<INF) 
				if(d[v] > d[u]+map[u][v]) //對每個點依次進行鬆弛操作 
					d[v] = d[u]+map[u][v];
	}
	printf("ans = "); 
	for(i=1;i<=n;i++)
		printf("%d ",d[i]);
}
int main(){	
	scanf("%d %d",&n,&m);
	for(int i = 1; i <= n; i++)
		for(int j =1; j<=n; j++)
			if(i == j) map[i][j] = 0;
			else map[i][j] = INF;
	for(int i = 1,u,v,w; i <= m; i++){
		scanf("%d %d %d",&u,&v,&w);
		//printf("%d %d %d\n",u,v,w);
		map[u][v] = w;
	}
	dijstra();
	return 0;
 
}
/*
5 9 1
1 2 10
1 3 3
2 3 1
2 4 2
3 2 4
3 4 8 
3 5 2
4 5 7
5 4 9 
ans:0 7 3 9 5 
*/

【使用堆(priority_queue)優化+vector存圖】

/*****************************
*author:ccf
*source:
*topic:shortest_path_dijkstra_que
*tip:鄰接表使用vector
*******************************/
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <cmath>
#include <queue> 
#define ll long long
using namespace std;

const int N = 5e5+7;
const int INF = 2147483647;
int n,m,s;
int dis[N]; //dis[i] 爲源點到i點的最短路徑 
bool vis[N] = {0};//vis[i] = 1,證明 i 已經確定了最短路 
struct Node{
	int v;//端點的編號 
	int w;//權值 
	bool operator < (const Node& b) const{
		return w > b.w;   //priority_queue總是將最大的元素出列
	}
}node;
vector<vector<Node> > G; 

void dijkstra(int s){
	priority_queue<Node> pq;
	node.v = s;//放入起點 
	node.w = 0;
	pq.push(node);
	dis[s] = 0;
	while(pq.size()){
		Node tp = pq.top();pq.pop();//獲取堆頂
		int u = tp.v,vv; //u是已經確認最短路徑的點編號,vv是沒有確認的點的編號 
		if(vis[u]) continue;
		vis[u] = true;
		int len = G[u].size();
		for(int i = 0; i < len; ++i){
			vv = G[tp.v][i].v;
			if(vis[vv]) continue;
			if(dis[vv] > dis[u] + G[u][i].w){//鬆弛操作 
				dis[vv] = dis[u] + G[u][i].w;
				node.v = G[u][i].v,node.w = dis[vv];
				pq.push(node);
			}
		}
	}
}

int main(){
	freopen("data.in","r",stdin);
	scanf("%d %d %d",&n,&m,&s);
	G.resize(n+7);
	G.clear();
	for(int i = 1; i <= n; i++) dis[i] = INF;
	for(int i = 1,u,v,w; i <= m; ++i){
		scanf("%d %d %d",&u,&v,&w);
		node.v = v;
		node.w = w;
		G[u].push_back(node);
	}
	dijkstra(s);
	for(int i = 1; i <= n; ++i)
		printf("%d ",dis[i]);
	return 0;
}

【使用堆(priority_queue)優化+鏈式前向星存圖】

/*****************************
*author:ccf
*source:POJ-
*topic:
*******************************/
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <queue> 
#define ll long long
using namespace std;

const int N = 1e4+7,M = 5e5+7;
const int INF = 2147483647;
int n,m,s;
int dis[N]; //dis[i] 爲源點到i點的最短路徑 
bool vis[N] = {0};//vis[i] = 1,證明 i 已經確定了最短路 
int head[N],nex[M],to[M],eg[M],cnt_e = 0;
struct Node{
	int v;//端點的編號 
	int w;//權值 
	bool operator < (const Node& b) const{
		return w > b.w;   //priority_queue總是將最大的元素出列
	}
}node;
void addedge(int u,int v,int w){
	nex[++cnt_e] = head[u];
	head[u] = cnt_e;
	to[cnt_e] = v;
	eg[cnt_e] = w;
}
void dijkstra(){
	for(int i = 1; i <= n; i++) dis[i] = INF;
	priority_queue<Node> pq;
	node.v = s;
	node.w = 0;
	pq.push(node);
	dis[s] = 0;
	while(pq.size()){
		int u = pq.top().v;
		pq.pop();
		if(vis[u]) continue;
		vis[u] = true;
		for(int i = head[u],vv; i; i = nex[i]){
			vv = to[i];
			if(dis[vv] > dis[u] + eg[i]){
				dis[vv] = dis[u] + eg[i];
				//node.w = dis[vv];node.v = vv;
				pq.push((Node){vv,dis[vv]});
			}
		}
	}
}

int main(){
	//freopen("data.in","r",stdin);
	scanf("%d %d %d",&n,&m,&s);
	for(int i = 1,u,v,w; i <= m; ++i){
		scanf("%d %d %d",&u,&v,&w);
		addedge(u,v,w);
	}
	dijkstra();
	for(int i = 1; i <= n; ++i)
		printf("%d ",dis[i]);
	return 0;
}

例題:洛谷 P3371 【模板】單源最短路徑(弱化版)https://www.luogu.com.cn/problem/P3371

發佈了132 篇原創文章 · 獲贊 54 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章