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

文章目录

前言

由于图论学习总结内容过多,全放在一篇博客过于冗长现进行拆分,本文是最短路 部分,其他部分地址见:图论学习总结(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)(最短路加动态规划)

D-Tokitsukaze and Slash Draw_2024牛客寒假集训营2 (% n n n​下的最短路 or DP)

1020-胖胖的牛牛 (nowcoder.com) (拆点建图思想)

1941 G − C o d e f o r c e s 1941G - Codeforces 1941G−Codeforces ( r a t i n g 2000 rating\ 2000 rating 2000)

相关推荐
梧桐树04292 小时前
python常用内建模块:collections
python
Dream_Snowar2 小时前
速通Python 第三节
开发语言·python
XH华2 小时前
初识C语言之二维数组(下)
c语言·算法
南宫生3 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
不想当程序猿_3 小时前
【蓝桥杯每日一题】求和——前缀和
算法·前缀和·蓝桥杯
落魄君子3 小时前
GA-BP分类-遗传算法(Genetic Algorithm)和反向传播算法(Backpropagation)
算法·分类·数据挖掘
菜鸡中的奋斗鸡→挣扎鸡3 小时前
滑动窗口 + 算法复习
数据结构·算法
蓝天星空3 小时前
Python调用open ai接口
人工智能·python
Lenyiin3 小时前
第146场双周赛:统计符合条件长度为3的子数组数目、统计异或值为给定值的路径数目、判断网格图能否被切割成块、唯一中间众数子序列 Ⅰ
c++·算法·leetcode·周赛·lenyiin
jasmine s4 小时前
Pandas
开发语言·python