图论随手记 - 最短路
_RainCappuccino_ · · 个人记录
顺序有点乱,后续会排一下,然后分板块整理
All
-
最短路算法的选择:
$n \le 4 \times 10 ^ 5$: dijkstra 尽量不用 SPFA。 -
神秘 IDEA: 一个带负权图,绝对最短路定义为,绝对值最小的最短路,求
s \to t 的绝对最短路。 -
最短路中,任意一个点的前缀都是最短路。
具体的:
u \to v 的最短路中有一个点w ,那么u \to w 的最短路,也在此条路径中。
Dijkstra
-
Dijkstra = 贪心 + DP 时间复杂度:
\text{O}(m \log m) -
证: 在 Dijkstra 算法过程中,每次找出
dis 最小的没有被确定最短路的点,它的 dis 一定是源点到它的最短路。注意:正难则反
\to 反证法证明: 设
S 集合表示最短路已经确定的点,T 集合表示最短路还没有确定的点。即证:
T 集合中dis 最小的元素可以直接加入到S 集合中。所以
dis 中储存的数值, 为从源点到u ,只经过S 集合中的点所得到的最短路。反证:
设当前
T 集合中dis 最小的元素为u ,且dis_{u} 不为源点到u 的最短路。如果有一条更短的最短路, 那么,这两条路径中至少要有一个点不同。
设最早不同的点为,
v_1 和v_2 ,v_2 为更短最短路中的一个点,v_1 为当前路径中的一个点。当且仅当,
v_2 在T 集合中,才会没有被更新到。又因为,dijkstra 只能处理非负权图。(这里也可以说明为什么 dijkstra 只能处理非负权图)
所以,
dis_{v_2} 只能小于dis_u 才能更新到u 。(如果有负权,这里会出问题)发现,这与
dis_u 为T 集合中最小的元素矛盾。所以,不存在
v_2 。得证
-
有的时候,dijkstra 堆优化是负优化。
Eg.
m = n ^ 2 时间复杂度为:
O(n^2 \log n^2) ,不如暴力的O(n ^ 2) 。
Bellman Ford
-
Bellman Ford(可优化为容易被卡的 SPFA)
优点: 可以处理负权,简单
缺点: 慢
时间复杂度:
\text{O}(nm)
Floyd
-
有关 Floyd 的题目主要需要考虑 Floyd 中能提供的信息,而不能只看到 Floyd 提供的任意两点之间的最短路或者是传递闭包。 (TODO: Floyd 深度理解 【坑 ++】)
-
Floyd 跑3遍,任意循环顺序都可以求出全源最短路。
-
证明: Floyd 的
k, i, j 枚举顺序为什么可行?回归 Floyd 的本初,原本为 DP,状态为
dp[k][i][j] :仅前k 个点,i \to j 的最短路。dp[k][i][j] = \min(dp[k][i][j], dp[k - 1][i][k] + dp[k - 1][k][j]) 此时,显然可行。
怎么滚动掉第一维的?
在 DP 的角度上看,这样的滚动是会使用到更新过的值,而非使用上一轮的数据。
但是,这里
dp_{k, i, k} 由于其中不经过k ,所以等价于dp_{k - 1,i,k} ,覆盖了也没关系,所以只要原先状态与覆盖状态意义相同,则同样可以使用滚动数组。 -
Floyd 的中间过程,也有一定意义。
-
Q: Floyd 可以用来判断负环吗?
A: 可以,跑两次 Floyd,在负环上的点则
dis 一直在减少。
建图思路
-
拆点
Eg. Paired Payment
如果这里没有权值为
(w_{a,b} +w_{b,c})^2 的限制的话, 那么就可以把一个点拆成两个点(也就是两层图),然后对于每一条边也拆成两条边:e(u_1, v_2), e(u_2, v_1) 那么这样就保证了走到1 层一定会经过两条边,所以最后答案取该层即可。对于一定要走k 条边也可以这样处理。但是这里有这个平方的限制。
再看一眼数据范围
w \le 50 ,所以可以根据这个建边了。第
0 层为原图(不连边),1 \to k 层表示走到这个点经过的上一条边的边权为k ,然后对于u 以及它连接的点v 建立e({(u, 0), (v, w)}, 0) 然后再对于u ,枚举所有边权,建立e((u, i), (v, 0), (i * w_{u, v}) ^ 2) ,跑最短路即可。最后答案即为走到s,0 时的权值。Eg. 传送卷轴
这里需要处理一下传送。显然,直接连边不太现实,边数过于大了。
考虑传送的限制
abs(dis_x - dis_y) = k 。发现这里暴力连接的所有边是有重复部分的,所有同一深度的点都会指向
dis_i + k 和dis_i - k 的所有点,考虑将重复部分不再反复建边,这时候建立n 个中转点dis_i + n ,每次先走到dis_{i \pm k} 这个点,然后通过这个点走到这一深度的所有点,所以只需要连接u, dis_{u \pm k} +n 和dis_i 到这一深度的所有点即可。形如: