前置知识:
dijkstra \text{dijkstra} dijkstra 模板
简要题意:
求任意两点的最短路。图中可能有 负环,负权,重边,自环 等现象。
显然我们先建图。
算法一
对于 20 % 20\% 20% 的数据, 1 ≤ n ≤ 100 1\leq n \leq 100 1≤n≤100,不存在负环(可用于验证 Floyd \text{Floyd} Floyd 正确性)
嗯,出题人都告诉你用 Floyd \text{Floyd} Floyd 了,而且 Floyd \text{Floyd} Floyd 的代码简洁得出奇,所以这 20 p t s 20pts 20pts 完全是送给你啦!
时间复杂度: O ( n 3 ) O(n^3) O(n3).
实际得分: 20 p t s 20pts 20pts.
算法二
对于另外 20 % 20\% 20% 的数据, w ≥ 0 w \geq 0 w≥0(可用于验证 Dijkstra \text{Dijkstra} Dijkstra 正确性)
出题人真善良,算法都告诉你了。
Dijkstra \text{Dijkstra} Dijkstra 跑 n n n 次,然后队列优化(不优化肯定连部分分都拿不到了)
时间复杂度: O ( n m log n ) O(nm \log n) O(nmlogn).
实际得分: 40 p t s 40pts 40pts.(结合前面的 Floyd \text{Floyd} Floyd)
算法三
我们基于 Dijkstra \text{Dijkstra} Dijkstra 的思路思考,因为这个思路不会超时,我们需要想一个办法解决 负环 问题。
可以想到的一种办法,把所有边权加上一个值使得不存在负权,然后再减回去。但是 理想是美好的,现实是残酷的 这样贪心完全错了。
比方说:
你先把每条边 + 3 +3 +3 变成:
求出最短路为 0 0 0. 然而正确的答案是 − 4 -4 −4,你怎么减也减不出这个值。其错误在于,假设走了多条边的最短路与另一条走了较少边的次短路,加上值之后最短路就不一定还是最短路了。
这样要是能解决我们还要 Johnson \text{Johnson} Johnson 干么
他提出了一种思想,解决这种问题:
-
构造一个“上帝节点” 0 0 0 号,并且建边 ( 0 , i , 0 ) ( 1 ≤ i ≤ n ) (0,i,0) (1 \leq i \leq n) (0,i,0)(1≤i≤n).(即向每个点建一条权值为 0 0 0 的边)
-
用 SPFA \text{SPFA} SPFA 一遍求出 h i h_i hi 表示 0 0 0 号节点到 i i i 号节点的最短路(顺便可以判负环)。
-
此时如果有一条 u → v u \rightarrow v u