dijkstra算法详解

dijkstra用于在带权无负环的图中寻找单源最短路径。

怎么样,读完了这句话之后是不是很晕?其实很好理解,解释一下两个新名词,无负环就是没有负数的环,例如:

图中就存在负环,实际上在这个图中无法存在最短路的定义,例如要求从B到C的最短路,先经过A绕一圈答案为 \(-5\),但是再绕一圈答案为 \(-7\),再绕一圈答案为 \(-9\)……显然,在存在负环的情况下,难以存在最短路的定义。

单源最短路很好理解,就是从一个点到其他点的最短路,而全源最短路为从任一点到其他点的最短路。

dijkstra算法思路

利用贪心策略,先给个图

请注意!从A点到B点的最短路不一定是A与B的直接路径,例如图中从1点到2点的最短路为 \(2\) 而不是 \(3\),可以先经过5号点在到达2号点。

dijkstra算法的主要思路就是从一个点出发对其他节点不停做松弛操作。

什么是松弛操作呢?听起来好高大上,其实说白了就是句废话:如果此时到达这个点的最短路比原来更短就更新。

例如从1号点出发,用 \(dis_i\) 表示到达 \(i\) 点的最短路径,初始时图为(圆内用序号+\(dis_i\) 表示当前点,如第一个点的 \(dis_i\) 为5表示为 \(1-5\) ):

此时我们发现1号点可以对2、3、4、5号点进行松弛操作,于是便将 \(dis_4\) 更新为 \(2\),将 \(dis_5\) 更新为 \(1\),将 \(dis_2\) 更新为 \(3\),将 \(dis_3\) 更新为 \(4\),再用这一轮得到的点继续去松弛别的点。

是的,一轮松弛就结束了!dijkstra算法的原理就是一直重复以上操作直到无法松弛为止。

代码实现

#include<bits/stdc++.h>
using namespace std;
const int N=5e3+5;
int a[N][N],dis[N],n,m,s;
bool vis[N];
priority_queue<int> q;
void dfs(int beg){
	while(1){
		int k=0;
		for(int i=1;i<=n;i++)
			if(!vis[i]&&dis[i]<=dis[k]) k=i;//寻找最小值
		if(!k) return ;//无法继续松弛就返回
		vis[k]=1;//标记
		for(int i=1;i<=n;i++){
			if(dis[i]>=dis[k]+a[k][i]){
				dis[i]=dis[k]+a[k][i];//松弛
			}
		}
	}
}
int main(){
	memset(a,0x3f,sizeof a);
	cin>>n>>m>>s>>e;//n个节点,m条边,s为起点,e为重点
	for(int i=1;i<=m;i++){
		int u,v,w;
		cin>>u>>v>>w;
		if(u==v) continue;//删自环
		a[u][v]=min(a[u][v],w);//删重边
	}
	memset(dis,0x3f,sizeof dis);
	dis[s]=0;//初始化
	dfs(s);
	cout<<dis[e];
	return 0;
}

该算法的时间复杂度为 \(O(n^2)\)

很明显这个算法的时间复杂度我们是无法接受的,考虑优化。

代码中动态维护了最小值,这个算法可以用堆来实现,不会使用堆的可以参考这篇博客

解决了这个问题,该算法的时间复杂度可以降到 \(O(n log_n)\)

代码实现

#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5;
int n,m,s;
struct node{
	int to,val;
	bool friend operator < (const node &x,const node &y){
		return x.val>y.val;//重载运算符
	}
};
vector<node> a[N];
int dis[N];
bool vis[N];
void dijkstra(){
	priority_queue<node> q;//使用STL的优先队列实现堆
	q.push({s,0});
	while(!q.empty()){
		node t=q.top();
		q.pop();
		if(!vis[t.to]){
			vis[t.to]=true;
			for(auto p:a[t.to]){
				if(dis[p.to]>dis[t.to]+p.val){
					dis[p.to]=dis[t.to]+p.val;//进行松弛操作
					q.push({p.to,dis[p.to]});//将新的节点加入队中
				}
			}
		}
	}
}
int main(){
	cin>>n>>m>>s>>e;
	memset(dis,0x3f,sizeof dis);
	dis[s]=0;//初始化
	for(int i=1;i<=m;i++){
		int u,v,w;
		cin>>u>>v>>w;
		a[u].push_back({v,w});
	}
	dijkstra();
	cout<<dis[e];
	return 0;
}

以上为dijkstra算法的内容,掌握之后可以去做洛谷的33714779

posted @   very_easy  阅读(84)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示