[算法笔记] Dijkstra
updated. 2019.11.3
Dijkstra是用于求解正权图上的单源最短路径(SSSP)问题的算法
在这里我也想说一下关于SPFA和Dij Dij是一个求没有负权边的图的单源最短路的算法 SPFA是一个求存在负权边的图的单源最短路以及判负环的算法 看似一字之差 写起来也很像 但其实Dij和SPFA完全是不同的思想 Dij是贪心思想 利用了一个只有没有负权边的图才拥有的性质 而SPFA更像是一个"暴力"算法
另外 SPFA没死
下面是正文
算法流程
Dijkstra算法其实是一个基于贪心的过程
正确性会在后面提及
设起点是
由于其它点的
优化
寻找全局最小值这一步是可以优化的
加入元素、查找最小值、删除最小值
用堆来实现这些操作非常合适
所以就有了堆优化的Dijkstra
堆中每个元素存两个值 结点编号
另外 如果图中存在负权边 即使没有负环 Dijkstra 也会有错误
如果不用新数组判断而用距离
简单应用
在求最短路的过程中可以同时维护一些其他信息
这些信息也应该能够传递
1.记录路径
每次更新最短路的时候记一个pre即可 代表这条路径是由哪个点过来的
最后直接从最后一个点不断取pre 输出路径
void dijkstra(){
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[s] = 0; t.x = s; t.dis = 0; q.push(t);
while(!q.empty()){
h = q.top(); q.pop();
int u = h.x,v;
if(vis[u]) continue; vis[u] = 1;
for(int i = hed[u];i;i = nxt[i]){
v = to[i];
if(dis[v] > dis[u] + cst[i]){
dis[v] = dis[u] + cst[i];
pre[v] = u;
t.x = v; t.dis = dis[v];
q.push(t);
}
}
}
}
while(now){
printf("%d ",now);
now = pre[now];
}
2.最短路计数
记录一个
在
void dijkstra(int s){
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
memset(cnt,0,sizeof(cnt));
dis[s] = 0; cnt[s] = 1; t.x = s; t.dis = 0; q.push(t);
while(!q.empty()){
h = q.top(); q.pop();
int u = h.x; if(vis[u]) continue; vis[u] = 1;
for(int i = hed[u];i;i = nxt[i]){
int v = to[i];
if(dis[v] > dis[u] + w[i]){
dis[v] = dis[u] + w[i]; cnt[v] = cnt[u];
t.x = v; t.dis = dis[v];
q.push(t);
}
else if(dis[v] == dis[u] + w[i]){
cnt[v] += cnt[u];
cnt[v] %= N;
}
}
}
}
3.次短路
记录到每个点的最短路和次短路
这里由于边能够重复走 就不能够使用是否出过堆判断 一个点可能要更新其它点多次
这里只要用路程判断 就是堆中元素
还有一种神奇的解法 把每一条边再加一条边 起点终点相同 边权为原来的三倍 代表
void dijkstra(int fr){
memset(dis,0x3f,sizeof(dis)); dis[fr] = 0;
memset(ds2,0x3f,sizeof(ds2));
memset(vis,0,sizeof(vis));
priority_queue <node> q;
t.x = fr; t.dis = 0; q.push(t);
while(!q.empty()){
h = q.top(); q.pop();
int u = h.x; if(vis[u]) continue; vis[u] = 1;
for(int i = hed[u];i;i = nxt[i]){
int v = to[i];
if(dis[v] > dis[u] + w[i]){
ds2[v] = dis[v];
dis[v] = dis[u] + w[i];
t.x = v; t.dis = dis[v];
q.push(t);
}
else if(ds2[v] > dis[u] + w[i] && dis[v] != dis[u] + w[i]) ds2[v] = dis[u] + w[i];
if(ds2[v] > ds2[u] + w[i]) ds2[v] = ds2[u] + w[i];
}
}
}
for(int i = 1;i <= m;i ++){
scanf("%d %d %d",&a,&b,&c);
add_edge(a,b,c);
add_edge(b,a,c);
add_edge(a,b,3 * c);
add_edge(b,a,3 * c);
}