Dijkstra求最短路与严格次短路

Posted louis_11

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Dijkstra求最短路与严格次短路相关的知识,希望对你有一定的参考价值。

前言:

设 vis_i 表示第 i 个点是否更新过其他点;

设 dis_i 表示从起点 s 到达第 i 个点的最短距离;

设 sdis_i 表示从起点 s 到达第 i 个点的严格次短距离;

设 dis(u, v) 表示第 u 个点到达第 v 个点的距离;

最短路:

题目link:https://www.luogu.com.cn/problem/P4779

Dijkstra 的 O(n^2) 做法:

Dijkstra 的思想是每次找到一个 dis 值最小的并且 vis 值为 false 的一个点 i,并用 i 去更新其他点的 dis。

容易发现这是一个贪心的思想,并且很容易证明一个点更新完其他点后,它的 dis 值一定不会再重复被更新(它的 dis 值已经是没更新过其他点的点中最小的了,也就不可能再被那些点更新了)。

那么这样便可以得出 Dijkstra 的代码:

 1 void dijkstra(int s) {
 2 
 3     std::memset(dis, 0x3f, sizeof(dis));
 4     std::memset(vis, false, sizeof(vis));
 5 
 6     dis[s] = 0;
 7     int cur;
 8 
 9     for(int k = 1; k <= n; ++k) {
10 
11         int mindis = INF;
12         for(int i = 1; i <= n; ++i) {
13             if(dis[i] < mindis && !vis[i]) {
14                 mindis = dis[i];
15                 cur = i;
16             }
17         }
18 
19         vis[cur] = 1;
20 
21         for(int i = head[cur]; i; i = stu[i].nxt) {
22             int v = stu[i].to;
23             dis[v] = std::min(dis[v], dis[cur] + stu[i].val);
24         }
25 
26     }
27 
28     return;
29 }
Dijkstra的代码

但是众所周知,Dijkstra 算法无法处理负边权的情况,下面给出证明:

因为 Dijkstra 是利用了贪心的思想,可以得出一个点的更新完其他点后不会再被更新这个性质来求出所有点的 dis 值。但是当图上存在负边权的时候,容易发现此时这个性质无法得到保证,因为在 u 更新完 v 后,如果 dis(u, v) 是负值并且这是一条双向边,那么 v 还可以转过头来更新 u,这就导致 Dijkstra 算法出现错误,故 Dijkstra 算法无法处理负边权的情况。

Dijkstra 的 O(m log m) 做法:

考虑优化前面的 O(n^2) 做法。

可以发现前面的做法需要找到一个 dis 值最小的并且 vis 值为 false 的点,那么这里容易想到用一个小根堆去优化它。

每次更新完一个点就把这个点的编号和这个点的 dis 值扔进小根堆里面,可以用一个 pair 来存放它们(但是注意:pair 是优先按 first 来排序,因此要 dis 值在前,编号在后!!!)。

 

以上是关于Dijkstra求最短路与严格次短路的主要内容,如果未能解决你的问题,请参考以下文章

POJ 3255 Roadblocks (Dijkstra求最短路径的变形)(Dijkstra求次短路径)

a*算法求最短路径和floyd还有dijsktra算法求最短路径的区别?

dijkstra求最短路

Dijkstra算法求单源最短路

求最短路径(Bellman-Ford算法与Dijkstra算法)

用Dijkstra算法求最短路径