单源最短路 Dijkstra 学习笔记

算法实现

Dijkstra是通过贪心来进行实现的。所以不能判定有负边的图。
看一下这张图,我们从\(1\)号点开始遍历,且令\(dist_i\)为第\(i\)个点到\(1\)好点的最短路径。

graph LR A[1]--5-->B((2)) C((3))--6-->D((4)) E((5))--12-->F((6)) A--7-->C A--6-->D A--11-->E C--1-->F

首先我们把已知最短路的点设为长方形,未知的设为圆形即可,那么一号点就是已知的,及\(dist_1=0\)
接下来,从已知点集到未知点集最短的一个点为点\(2\),及\(dist_2=5\)
然后一步一步推,如图:

graph LR A[1]--5-->B[2] C((3))--6-->D((4)) E((5))--12-->F((6)) A--7-->C A--6-->D A--11-->E C--1-->F

然后是\(4\)号点最近

graph LR A[1]--5-->B[2] C((3))--6-->D[4] E((5))--12-->F((6)) A--7-->C A--6-->D A--11-->E C--1-->F

再就是\(3\)号点。

graph LR A[1]--5-->B[2] C[3]--6-->D[4] E((5))--12-->F((6)) A--7-->C A--6-->D A--11-->E C--1-->F

这样就可以推下去就可以了。
注意:最短的那个点并不是指边权,比如说\(6\)号点,它的值是由\(dist_3+c_{3,6}=8\),不仅仅是\(c_{3,6}\)

代码实现

#include<cstdio>//采用Dijstra 
#include<vector>//朴素 
#include<cstring>//使用vector动态数组存储 
#define maxn 500039
using namespace std;
int ans[maxn];
int n,m,s,i,j,from;
struct Di{
	int to,q;
}f;
vector<Di> q[maxn];
int v[maxn];
int T,minx,a,b;  
int main(){
    scanf("%d%d%d",&n,&m,&s);
    memset(ans,0x3f,sizeof(ans));
    ans[s]=0;
    for(i=1;i<=m;i++){
    	scanf("%d%d%d",&from,&f.to,&f.q);
    	q[from].push_back(f);
	}
	v[s]=1;
	for(int k=1;k<n;k++){//核心 Dijstra
	    minx=0x7fffffff;
		for(i=1;i<=n;i++)
			if(v[i])
			    for(j=0;j<q[i].size();j++)
			        if(ans[i]+q[i][j].q < minx && !v[q[i][j].to] ){
			        	minx=ans[i]+q[i][j].q;
						a=q[i][j].to;
					}
		v[a]=1;
		ans[a]=minx;
	}
	for(int i=1;i<=n;i++) 
	    if(ans[i]==0x3f3f3f3f)
	        printf("2147483647 ");
	    else printf("%d ",ans[i]);
    return 0;
}

堆优化-Update 20201106

这里会发现,我们这里有三层循环,大大降低了效率,所以我们需要使用堆来进行优化。
我们看一下Dijkstra的步骤:

  1. 设置起点
  2. 从已知未知集中找到最短的一条路径
  3. 更新这个点的最短路
  4. 重复24

我们发现第2步可以进行优化。
怎么优化呢?
我会zkw非递归式线段树!
我会斐波那契额堆!
我会堆!
我们把所有从已知点集到扩展出的所有点扔近一个堆。当然, \(dist\) 最小的在堆顶部就可以了。

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 100039
#define maxm 200039
using namespace std;
//#define debug
typedef int Type;
inline Type read(){
	Type sum=0;
	int flag=0;
	char c=getchar();
	while((c<'0'||c>'9')&c!='-') c=getchar();
	if(c=='-') c=getchar(),flag=1;
	while('0'<=c&&c<='9'){
		sum=(sum<<1)+(sum<<3)+(c^48);
		c=getchar();
	}
	if(flag) return -sum;
	return sum;
}
struct JTZ{
	int num,dist;
	bool operator > (const JTZ x) const {
		return this->dist > x.dist;
	}
};
priority_queue<JTZ,vector<JTZ>,greater<JTZ> > q;
int dist[maxn],vis[maxn],n,m,s;
int u,v,w;
int head[maxn],nex[maxm],to[maxm],c[maxm],k;
#define add(x,y,z) c[++k]=z;\
to[k]=y;\
nex[k]=head[x];\
head[x]=k;
void Dij(){
	q.push((JTZ){s,0});
	dist[s]=0;
	while(!q.empty()){
		int cur=q.top().num;
		q.pop();
		if(vis[cur]) continue;
		vis[cur]=1;
		for(int i=head[cur];i;i=nex[i])
		    if(c[i]+dist[cur]<dist[to[i]])
		        if(!vis[to[i]]){
		            dist[to[i]]=c[i]+dist[cur];
		        	q.push((JTZ){to[i],dist[to[i]]});
				}
	}
	return;
}
int main(){
    n=read(); m=read(); s=read();
    for(int i=1;i<=m;i++){
    	u=read(); v=read(); w=read();
    	add(u,v,w);
	}
	memset(dist,0x7f,sizeof(dist));
	Dij();
	for(int i=1;i<=n;i++)
	    printf("%d ",dist[i]);
	return 0;
}

posted @ 2021-02-15 21:17  jiangtaizhe001  阅读(43)  评论(0)    收藏  举报