最短路(图论学习总结部分内容)

文章目录

前言

由于图论学习总结内容过多,全放在一篇博客过于冗长现进行拆分,本文是最短路 部分,其他部分地址见:图论学习总结(For XCPC)

二、最短路

多源最短路

F l o y d Floyd Floyd​ 算法

​ 令 D [ i , j ] D[i,j] D[i,j] 表示**"经过若干个编号不超过k的结点"** 从 i i i 到 j j j 的最短路。该问题可以划分为两个子问题,经过编号不超过 k − 1 k-1 k−1 的结点从 i i i 到 j j j,或者先从 i i i 到 k k k,再从 k k k 到 j j j。于是:
D [ k , i , j ] = m i n ( D [ k − 1 , i , j ] , D [ k − 1 , i , k ] + D [ k − 1 , k , j ] ) D[k,i,j] = min(D[k-1,i,j], D[k-1,i,k]+D[k-1,k,j]) D[k,i,j]=min(D[k−1,i,j],D[k−1,i,k]+D[k−1,k,j])

​ 初值为 D [ 0 , i , j ] = G [ i , j ] D[0,i,j] = G[i,j] D[0,i,j]=G[i,j],可以看到 F l o y d Floyd Floyd 的本质是动态规划, k k k 是阶段。其中 k k k 这一维可以省略,即 D [ i , j ] = m i n ( D [ i , j ] , D [ i , k ] , D [ k , j ] ) D[i,j] = min(D[i,j],D[i,k],D[k,j]) D[i,j]=min(D[i,j],D[i,k],D[k,j])。

例题及变形
e g 1 : S o r t i n g I t A l l O u t eg1:Sorting\ It\ All\ Out eg1:Sorting It All Out ( 蓝书例题,传递闭包 ) (蓝书例题,传递闭包) (蓝书例题,传递闭包)

题目大意:

​ 这是一道传递闭包问题,是Floyd算法的简单应用。我们可以对每个形如 i < j i < j i<j 的不等式,令 d [ i , j ] = 1 d[i,j] = 1 d[i,j]=1,除了 i < j i<j i<j 的其他情况都令 d [ i , j ] = 0 d[i,j]=0 d[i,j]=0。我们可以用Floyd算法对 d d d 求传递闭包,若存在 d [ i , j ] = d [ j , i ] = 1 d[i,j] = d[j,i] = 1 d[i,j]=d[j,i]=1 则说明有矛盾,若均为0则说明不能确定关系。此时,我们可以再用二分法,二分 t t t 的值,判断仅用前 t t t 个不等式能否判断所有关系。

e g 2 : S i g h t s e e i n g t r i p eg2:Sightseeing\ trip eg2:Sightseeing trip ( 蓝书例题, F l o y d 求最小环 ) (蓝书例题,Floyd求最小环) (蓝书例题,Floyd求最小环)

题目大意:

​ 我们考虑 F l o y d Floyd Floyd 的算法过程。当外层循环 k k k 刚开始时, d [ i , j ] d[i,j] d[i,j] 保存着经过编号不超过 k − 1 k-1 k−1 的结点从 i i i 到 j j j 的最短路长度。于是, min ⁡ 1 ≤ i < j < k { d [ i , j ] + g [ j , k ] + g [ k , i ] } \min_{1\leq i<j<k}{\{d[i,j]+g[j,k]+g[k,i]\}} 1≤i<j<kmin{d[i,j]+g[j,k]+g[k,i]} 就是满足以下两个条件的最小环长度:1、由编号不超过 k k k 的结点构成。2、经过结点 k k k。前面式子中的 i , j i,j i,j 相当于枚举了环上与 k k k 相邻的两个点,对 A ∈ [ 1 , n ] A∈[1,n] A∈[1,n] 都进行计算,取最小值即是答案(我们在考虑每个 k k k 只考虑了由编号不超过 k k k 的结点构成的最小环,由对称性可知,这样不会影响答案)。

e g 3 : C o w R e l a y s eg3:Cow\ Relays eg3:Cow Relays ( 蓝书例题,倍增优化 D P o r 广义矩阵乘法 + 快速幂优化 ) (蓝书例题,倍增优化DP\ or\ 广义矩阵乘法+快速幂优化) (蓝书例题,倍增优化DP or 广义矩阵乘法+快速幂优化)

题目大意:

​ F l o y d Floyd Floyd 算法是一种以"途径点集大小"为阶段的动态规划算法,在这道题我们可以考虑用以边的数量为阶段的 D P DP DP方法经过倍增优化求解。首先,对于点的编号我们需要先进行离散化,假设离散化后点的数量为 P P P, G G G 为 P ∗ P P*P P∗P 的邻接表。我们考虑从 s s s 出发分别经过 2 0 , 2 1 , . . . , 2 N 2^0,2^1,...,2^N 20,21,...,2N 条边到 e e e 的最短路,倍增优化的前提是状态可以按阶段任意划分,所以我们不妨令 F [ i , x , y ] F[i,x,y] F[i,x,y] 表示从 x x x 出发恰好经过 2 i 2^i 2i 条边到达 y y y。那么状态转移时的决策,我们只需要枚举中转点 k k k,状态转移方程: F [ i , x , y ] = min ⁡ 1 ≤ k ≤ t o t { F [ i , x , y ] , F [ i − 1 , x , k ] + F [ i − 1 , k , y ] } F[i,x,y] = \min_{1\le k\le tot} \{F[i,x,y], F[i-1,x,k]+F[i-1,k,y] \} F[i,x,y]=min1≤k≤tot{F[i,x,y],F[i−1,x,k]+F[i−1,k,y]}。

​ 我们进一步考虑,其实这道题是一种广义的矩阵乘法,可以通过矩阵快速幂优化。我们考虑以下式子 ∀ i , j ∈ [ 1 , P ] B [ i , j ] = min ⁡ 1 ≤ k ≤ P { G [ i , k ] + G [ k , j ] } \forall i,j\in[1,P]\ \ B[i,j]=\min_{1\le k\le P}\{G[i,k]+G[k,j] \} ∀i,j∈[1,P] B[i,j]=min1≤k≤P{G[i,k]+G[k,j]},在式子中我们枚举了所有的中专点 k k k,对于每一对 ( i , j ) (i,j) (i,j) 来说, B B B 记录的是恰好经过两条边的最短路。

​ 若 G m G^m Gm 记录了所有点两两之间恰好经过 m m m 条边的最短路,我们可以初始化 a n s = G 0 ans=G^0 ans=G0 , a n s [ i ] [ j ] ans[i][j] ans[i][j] 表示恰好经过了 0 0 0 条边从 i i i,到 j j j 的最短路。那么: ∀ i , j ∈ [ 1 , P ] , D r + m = min ⁡ 1 ≤ k ≤ P { ( D r ) [ i , k ] + ( D m ) [ k , j ] } \forall i,j \in[1,P],\ D^{r+m} = \min_{1\le k\le P}\{(D^r)[i,k]+(D^m)[k,j] \} ∀i,j∈[1,P], Dr+m=min1≤k≤P{(Dr)[i,k]+(Dm)[k,j]},这个式子其实等价于一个关于 min ⁡ \min min和 + + +的广义的"矩阵乘法",我们可以先用快速幂计算出 a n s N ans^N ansN,最后 ( a n s N ) [ S , E ] (ans^N)[S,E] (ansN)[S,E] 就是答案,每次矩阵乘法是 O ( T 3 ) O(T^3) O(T3),一共 log ⁡ N \log N logN 次,时间复杂度为 O ( T 3 log ⁡ N ) O(T^3\log N) O(T3logN)​。

​ ac代码参考:代码查看 (nowcoder.com)

练习题

Gym-104023-Problem - F(2022 C C P C CCPC CCPC威海F,金牌题,Floyd +建图+用类似 P r i m Prim Prim 的做法,找出以每个点 s 为根的有向瓶颈生成树)

单源最短路

知识点
  • D i j k s t r a Dijkstra Dijkstra
  • S P F A SPFA SPFA 和 B e l l m a n − F o r d Bellman-Ford Bellman−Ford( S P F A SPFA SPFA 已死)
  • 01 B F S 01BFS 01BFS(只适用于边权只有0和1的图)
  • 拓扑排序 + D P 拓扑排序+DP 拓扑排序+DP (有向无环图)
例题及变形
e g 1 : 最优贸易 eg1: 最优贸易 eg1:最优贸易 (B-最优贸易_)

题目大意:

​ 本题是让我们在一张结点带有权值的图上找出一条从 1 1 1 到 n n n 的路径,使得路径上能够找出两个点 p , q p,q p,q, v a l p − v a l q val_p-val_q valp−valq 最大。

​ 我们可以把这张图当作有向图处理,从结点 1 1 1 出发跑一次 "最短路" 算法,预处理出当前结点 x x x 的前驱结点中所有点权的最小权值,记为 p r e [ x ] pre[x] pre[x],同理我们可以反向建图从 n n n 出发预处理出在正图中当前结点 x x x 之后所有点权的最大值记为 p a s t [ x ] past[x] past[x],最后枚举每个结点 x x x,用 p a s t [ x ] − p r e [ x ] past[x]-pre[x] past[x]−pre[x] 更新答案即可。

​ 注意,上述预处理中的 "最短路" 算法可以将松弛操作分别改为 min ⁡ 和 max ⁡ \min和\max min和max 那么松弛更新的操作就不满足 D i j k s t r a Dijkstra Dijkstra 的贪心性质,我们可以用 S P F A SPFA SPFA 算法。

e g 2 : R o a d a n d P l a n e s eg2:Road\ and\ Planes eg2:Road and Planes​ (C-Roads and Planes_0x61 图论-最短路)

题目大意:

​ 这道题是一个十分明显的单源点最短路问题,但图中带有负权边,我们不能直接使用 D i j k s t r a Dijkstra Dijkstra,又因为有特殊构造的数据,所以我们使用 S P F A SPFA SPFA 会 T L E TLE TLE。这时候我们就要注意到**双向边都是非负的,只有单向边可能是负的,并且单向边不构成环。**我们可以利用这一性质解决本题。

​ 如果我们只看双向边,会发现是一个个的连通块,连通块内所有边均为非负边权。那么我们可以对每一个连通块内跑 D i j k s t r a Dijkstra Dijkstra 算法。这时候如果我们把每一个连通块看作一个点的话,那么加上单向边(航线),那么整个图就是一张有向无环图,不管边权正负,我们都可以通过 拓扑排序 + 松弛操作 拓扑排序+松弛操作 拓扑排序+松弛操作在线性时间内求出最短路。

​ 至此,这道题的解法已经很明显了, 拓扑排序套 D i j k s t r a 拓扑排序套Dijkstra 拓扑排序套Dijkstra。我们在外层维护每一个连通块的度数 d e g deg deg,在每个块内跑 D i j k s t r a Dijkstra Dijkstra,遇到负权单向边,则更新连通块的度数用于外层的拓扑排序。

​ 整个的时间复杂度为 O ( T + P + R log ⁡ T ) O(T+P+R\log T) O(T+P+RlogT) 具体实现:代码查看 (nowcoder.com)

e g 3 : T h e r e a n d B a c k A g a i n eg3:There\ and\ Back\ Again eg3:There and Back Again 2024 I C P C 2024ICPC 2024ICPC亚太锦标赛 J

题目大意

​ 这是一道最短路的变形,题目要求从 1 1 1 到 n n n 再到 1 1 1 的最短距离,并且要求 1 1 1 到 n n n 和 n n n 到 1 1 1 的路径不相同。

首先,我们考虑最短路,要想总和最小那么去和回至少有一条是最短路,不妨就去时走最短路,那么 1 1 1 到 n n n,我们可以直接在图中跑 D i j k s t r a Dijkstra Dijkstra 算法记为 d 1 d1 d1,考虑回的时候,我们可以再以 n n n 为起始点跑一边最短路,记为 d 2 d2 d2,我们可以枚举每一条边,如果不在去的路径中,我们用 d 1 [ x ] + d 2 [ y ] + w d1[x]+d2[y] + w d1[x]+d2[y]+w 更新答案,其最小值就是我们需要的答案。

​ 实现细节:C++可以用链式前向星存图,边的话就有对应下标,开个数组标记即可,python的话用邻接表存图,直接用set当vis即可,ac代码参考( P y t h o n Python Python​​):Submission #256743872 - Codeforces

e g 5 : eg5: eg5: [ N O I P 2017 ] [NOIP2017] [NOIP2017]P3953 NOIP2017 提高组] 逛公园 - 洛谷 | 计算机科学教育新生态

题目大意

策策同学特别喜欢逛公园。公园可以看成一张 N N N 个点 M M M 条边构成的有向图 ,且没有自环和重边。其中 1 1 1 号点是公园的入口, N N N 号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要花的时间。

策策每天都会去逛公园,他总是从 1 1 1 号点进去,从 N N N 号点出来。

策策喜欢新鲜的事物,它不希望有两天逛公园的路线完全一样,同时策策还是一个 特别热爱学习的好孩子,它不希望每天在逛公园这件事上花费太多的时间。如果 1 1 1 号点 到 N N N 号点的最短路长为 d d d,那么策策只会喜欢长度不超过 d + K d + K d+K 的路线。

策策同学想知道总共有多少条满足条件的路线,你能帮帮它吗?

为避免输出过大,答案对 P P P 取模。如果有无穷多条合法的路线,请输出 − 1 -1 −1。

输入格式

第一行包含一个整数 T T T, 代表数据组数。

接下来 T T T 组数据,对于每组数据: 第一行包含四个整数 N , M , K , P N,M,K,P N,M,K,P,每两个整数之间用一个空格隔开。

接下来 M M M 行,每行三个整数 a i , b i , c i a_i,b_i,c_i ai,bi,ci,代表编号为 a i , b i a_i,b_i ai,bi 的点之间有一条权值为 c i c_i ci 的有向边,每两个整数之间用一个空格隔开。

输出格式

输出文件包含 T T T 行,每行一个整数代表答案。

【测试数据与约定】

对于不同的测试点,我们约定各种参数的规模不会超过如下

测试点编号 T T T N N N M M M K K K 是否有 0 0 0 边
1 1 1 5 5 5 5 5 5 10 10 10 0 0 0
2 2 2 5 5 5 1 0 3 10^3 103 2 × 1 0 3 2\times 10^3 2×103 0 0 0
3 3 3 5 5 5 1 0 3 10^3 103 2 × 1 0 3 2\times 10^3 2×103 50 50 50
4 4 4 5 5 5 1 0 3 10^3 103 2 × 1 0 3 2\times 10^3 2×103 50 50 50
5 5 5 5 5 5 1 0 3 10^3 103 2 × 1 0 3 2\times 10^3 2×103 50 50 50
6 6 6 5 5 5 1 0 3 10^3 103 2 × 1 0 3 2\times 10^3 2×103 50 50 50
7 7 7 5 5 5 1 0 5 10^5 105 2 × 1 0 5 2\times 10^5 2×105 0 0 0
8 8 8 3 3 3 1 0 5 10^5 105 2 × 1 0 5 2\times 10^5 2×105 50 50 50
9 9 9 3 3 3 1 0 5 10^5 105 2 × 1 0 5 2\times 10^5 2×105 50 50 50
10 10 10 3 3 3 1 0 5 10^5 105 2 × 1 0 5 2\times 10^5 2×105 50 50 50

对于 100 % 100\% 100% 的数据, 1 ≤ P ≤ 1 0 9 1 \le P \le 10^9 1≤P≤109, 1 ≤ a i , b i ≤ N 1 \le a_i,b_i \le N 1≤ai,bi≤N, 0 ≤ c i ≤ 1000 0 \le c_i \le 1000 0≤ci≤1000。

数据保证:至少存在一条合法的路线。


  • 2019.8.30 增加了一组 hack 数据 by @skicean
  • 2022.7.21 增加了一组 hack 数据 by @djwj233

题目分析

​ 关于方案数的统计,我们考虑动态规划,令 F [ y ] [ k ] F[y][k] F[y][k]表示从起点到 y y y,比最短路多 k k k 的长度的路径方案数。状态转移: F [ y ] [ k ] = ∑ d i s t [ x ] + t + w ( x , y ) = d i s t [ y ] + k F [ x ] [ t ] F[y][k] = \sum_{dist[x]+t+w(x,y)=dist[y]+k} F[x][t] F[y][k]=∑dist[x]+t+w(x,y)=dist[y]+kF[x][t]。对于这样的转移,我们需要按照一定的顺序,我们发现我们的阶段是 k k k, 即我们要先算 F [ 1... n ] [ 0 ] F[1...n][0] F[1...n][0]再算 F [ 1... n ] [ 1 ] F[1...n][1] F[1...n][1],以此类推。

​ 我们不妨直接写成记忆化搜索,这样我们就可以忽略顺序的问题了。记忆化搜索的入口是直接从第 n n n​ 个点开始往前回溯,在返回时更新状态,我们需要一张与原图完全相反的图。

​ 关于无穷多合法路线的理解:图中含有 0 0 0 环,且和答案有关的路径有关系,这个需要单独判断。如果有 0 0 0 环,我们在记忆化搜索时会重复访问到 F [ y ] [ k ] F[y][k] F[y][k],我们在记忆化搜索中加一个访问标记即可。

启发

​ 关于最短路有要求多少条边的(如牛站),有路径条数计数或方案计数的(如习题 R O A D ROAD ROAD),或是要将 d i s t dist dist限制在某一区间里的(如上一道例题 P e r m u t a t i o n Permutation Permutation P u z z l e Puzzle Puzzle)一般都用到了动态规划递推求解,区别在于阶段和状态的表示与转移不同。可见对于类似的这类问题,我们一般会通过分解为子问题的方式通过动规来求解,在比赛中遇到此类问题需要有一定的灵敏性。

欧拉回路
差分约束系统

​ 差分约束系统是一中特殊的 N N N元一次不等式组。它包含 N N N 个变量 X 1 X_1 X1~ X N X_N XN 以及 M M M 个约束条件,每个约束条件都形如 X i − X j ≤ c k X_i-X_j\le c_k Xi−Xj≤ck 其中 c k c_k ck 是常数。我们的问题就是求一组解 X 1 , X 2 , . . . , X N X_1,X_2,...,X_N X1,X2,...,XN 是所有的约束条件满足,或确定其无解。

e g 1 : eg1: eg1: P3275 SCOI2011 糖果 - 洛谷(差分约束)

题目大意

幼儿园里有 N N N 个小朋友, lxhgww \text{lxhgww} lxhgww 老师现在想要给这些小朋友们分配糖果,要求每个小朋友都要分到糖果。但是小朋友们也有嫉妒心,总是会提出一些要求,比如小明不希望小红分到的糖果比他的多,于是在分配糖果的时候, lxhgww \text{lxhgww} lxhgww 需要满足小朋友们的 K K K 个要求。幼儿园的糖果总是有限的, lxhgww \text{lxhgww} lxhgww 想知道他至少需要准备多少个糖果,才能使得每个小朋友都能够分到糖果,并且满足小朋友们所有的要求。

输入格式

输入的第一行是两个整数 N N N, K K K。接下来 K K K 行,表示这些点需要满足的关系,每行 3 3 3 个数字, X X X, A A A, B B B。

  • 如果 X = 1 X=1 X=1, 表示第 A A A 个小朋友分到的糖果必须和第 B B B 个小朋友分到的糖果一样多;
  • 如果 X = 2 X=2 X=2, 表示第 A A A 个小朋友分到的糖果必须少于第 B B B 个小朋友分到的糖果;
  • 如果 X = 3 X=3 X=3, 表示第 A A A 个小朋友分到的糖果必须不少于第 B B B 个小朋友分到的糖果;
  • 如果 X = 4 X=4 X=4, 表示第 A A A 个小朋友分到的糖果必须多于第 B B B 个小朋友分到的糖果;
  • 如果 X = 5 X=5 X=5, 表示第 A A A 个小朋友分到的糖果必须不多于第 B B B 个小朋友分到的糖果;

输出格式

输出一行,表示 lxhgww \text{lxhgww} lxhgww 老师至少需要准备的糖果数,如果不能满足小朋友们的所有要求,就输出 − 1 -1 −1。

对于 30 % 30\% 30% 的数据,保证 N ≤ 100 N\leq100 N≤100

对于 100 % 100\% 100% 的数据,保证 N ≤ 100000 N\leq100000 N≤100000

对于所有的数据,保证 K ≤ 100000 , 1 ≤ X ≤ 5 , 1 ≤ A , B ≤ N K\leq100000, 1\leq X\leq5, 1\leq A, B\leq N K≤100000,1≤X≤5,1≤A,B≤N


洛谷: upd 2022.7.6 \text{upd 2022.7.6} upd 2022.7.6:新添加 21 21 21 组 Hack 数据

ac 代码参考:代码查看 (nowcoder.com)

0 / 1 0/1 0/1 分数规划
A-Sightseeing Cows(nowcoder.com)(spfa判负环+二分求解01分数规划问题)
练习题

2023_ICPC_杭州站 - G - Codeforces(1/2个银牌题)

B-Intervals_0x65 图论-负环与差分约束(差分约束)

1044 − H A O I 2012 1044-HAOI2012 1044−HAOI2012\][ROAD (nowcoder.com)](https://ac.nowcoder.com/acm/contest/26077/1044)(最短路加动态规划) [D-Tokitsukaze and Slash Draw_2024牛客寒假集训营2](https://ac.nowcoder.com/acm/contest/67742/D) (% n n n​下的最短路 or DP) [1020-胖胖的牛牛 (nowcoder.com)](https://ac.nowcoder.com/acm/contest/26077/1020) (拆点建图思想) [1941 G − C o d e f o r c e s 1941G - Codeforces 1941G−Codeforces](https://codeforces.com/problemset/problem/1941/G) ( r a t i n g 2000 rating\\ 2000 rating 2000)

相关推荐
逢生博客19 分钟前
使用 Python 项目管理工具 uv 快速创建 MCP 服务(Cherry Studio、Trae 添加 MCP 服务)
python·sqlite·uv·deepseek·trae·cherry studio·mcp服务
堕落似梦26 分钟前
Pydantic增强SQLALchemy序列化(FastAPI直接输出SQLALchemy查询集)
python
黄昏ivi43 分钟前
电力系统最小惯性常数解析
算法
独家回忆3641 小时前
每日算法-250425
算法
烁3471 小时前
每日一题(小白)模拟娱乐篇33
java·开发语言·算法
坐吃山猪1 小时前
Python-Agent调用多个Server-FastAPI版本
开发语言·python·fastapi
Demons_kirit2 小时前
LeetCode 2799、2840题解
算法·leetcode·职场和发展
软行2 小时前
LeetCode 每日一题 2845. 统计趣味子数组的数目
数据结构·c++·算法·leetcode
永远在Debug的小殿下2 小时前
查找函数【C++】
数据结构·算法
Bruce-li__2 小时前
使用Django REST Framework快速开发API接口
python·django·sqlite