单源最短路 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的步骤:
- 设置起点
- 从已知未知集中找到最短的一条路径
- 更新这个点的最短路
- 重复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;
}