图论经典模型 听课笔记
下文主要梳理最短路的建图技巧,以及一些杂题。
1. 超级源点、超级汇点
模型 1.1(租车线路)
有若干条租车线路,表示可以花 块钱租一辆从城市 到城市 的车。
现在有甲、乙两人,甲从 A 到 C,乙从 B 到 D。两人在途中可以停留,也可以共租一辆车。求最小租车费用。
。
建图,连边
若两人的路径不交,分别从 A、B 出发,跑一边单源最短路即可。
否则,甲、乙两人的路径交集一定是连续的一段边,如下图:
(如果交集不连续,记最开始的公共点为 X,最后的公共点为 Y。分别统计一下甲、乙两人的路径中 X
记
记
建反图,在原图上跑一遍单源最短路即可求得
因此,只需在原图上新建超级源点 S 和超级汇点 T,对原图上每个点
时间复杂度为
模型 1.2
给定点集 S 和 T,要求从 S 中的每个点向 T 中的每个点连边权为的边,如何处理?
如果直接暴力连边,边数的复杂度为
但我们新建一个点
模型 1.3
次连边,每次从编号在 中的点向编号在 中的点连边权为 的边,如何处理?
考虑线段树优化建图。建两颗线段树
每次连边时,新建节点
时间复杂度为
2. 单源最小环
模型 2
求个点、 条边的带边权无向图中,包含 1 号点的最小环大小。
如何做到 ?
对与 1 号点相连的每条边
如何做到 ?
对原图 G 中任意两个相邻的点
考虑在原图 G 中,将 1 号点和与 1 号点相连的边删去,得到图 G'。
考虑 G 中每一个包含 1 号点的环,令环中与 1 号点相邻的点为
容易发现这与模型 1.1 中的表达式类似。于是考虑在图 G' 中新建源点 S 和汇点 T,对 G' 中每个点
但这么做会带来一个问题:可能求出的最短路为
为避免这种情况发生,考虑将点
但这种做法依然存在问题。记实际的最小环中,与 1 相连的点为
考虑到
对所有
如何做到 ?
在原图 G 中将 1 号点删去,得到新图 G'。在 G' 中加入超级源点 S。对 G 中 1 号点的所有邻点
在图 G' 中从 S 出发跑一遍单源最短路。在算法中,对每个点
观察 G 中每个经过 1 的环,在 G' 中对应一条
因此,只需枚举所有满足
做法来源:洛谷 P5304 [GXOI/GZOI2019] 旅行者
3. 分层图最短路
模型 3.1(跑图)
选择一条从出发的路径,最小化“路径长度 减 路径上前 大的点的点权和”的值。
, 。
发现将“路径上前
设
枚举
模型 3.2(P3953 [NOIP2017 提高组] 逛公园)
给定带边权的有向图,记从 1 号点到的最短路为 。
统计从到 ,且长度在 的范围内的路径条数,答案对 取模。
若有无数条符合要求的路径,输出。
, , , ,所有边权非负。
首先考虑边权均为正数的情况。
从 1 号点出发求单源最短路,记从 1 到
记
枚举
由于求最短路后,一条边
容易发现这与模型 3.1 类似。因此,类似分层图最短路,我们枚举
考虑怎么确定这个顺序。对所有
对
因此只需建出最短路图,并对其做拓扑排序即可。
但原题中还存在边权为
若一条路径经过零环上的任意一点,可以从该点出发绕零环走任意多圈再继续往下走,得到无数条路径。因此,只要有一条符合题目要求的路径经过零环上的点,则答案为
可以先通过一遍 dfs 求出所有零环上的点,则问题转化成是否存在在一条符合要求的路径经过零环上的点。
新建数组
初始时,对所有零环上的点
然后从这些点出发跑一遍 Dijkstra,记
4. 单源次短路
次短路分为两类,严格次短路和非严格次短路。非严格次短路径不能与最短路径相同,但是长度可以与最短路相等。而严格次短路径的长度必须严格小于最短路。
如何求出次短的简单路径?
众所周知,一定存在一个方案,使得最短路径为简单路径,但次短路径不一定。下文旨在说明,在所有的简单路径中,如何求次短路径。
下面的例题给出了一个求非严格次短简单路径的方法。
例题 4.1(洛谷 P1491 集合位置)
给定一个带边权的无向图,图中没有重边,但可能有自环。求图中从 1 号点到号点的非严格次短简单路径的长度。
若该路径不存在,输出。
, ,边权非负。
显然,由于最短路径和次短路径均为简单路径,因此最短路径中一定存在一条边,使得它在次短路径中未出现过。
因此做法呼之欲出。先跑一遍单源最短路,并记录最短路径经过的边。
考虑枚举最短路径中的边。对枚举的每条边,将其在原图中删去,并再跑一边单源最短路。(删边相互独立)
对枚举得到的答案求最小值,即为非严格次短简单路径的长度。
若使用朴素的 Dijkstra 算法,复杂度为
若使用堆优化的 Dijkstra 算法,复杂度为
至于如何求出严格的次短简单路径,本人不会,因此略。
上文说明了对所有简单路径,如何求次短路。下文将探讨对所有路径,如何求次短路。
如何求出次短路径?
下面的例题给出了一个求严格次短路径的方法。
例题 4.2(洛谷 P2865 [USACO06NOV] Roadblocks G)
给定一个带边权的无向图,求图中从到 严格次短路径的长度。
, ,边权均为正数。
考虑对 Dijkstra 算法进行变形。开两个数组
对当前正在枚举的点
- 若
,则令 , 。 - 否则,若
,令 。 - 否则,令
。
但这样做会带来问题。考略 Dijkstra 的正确性证明,算法当前枚举的点
但我们进行上述算法时,却只考虑了
考虑改进上述算法。发现
若使用朴素的 Dijkstra,时间复杂度为
若使用堆优化的 Dijkstra,时间复杂度为
非严格次短路的做法也类似,只需将上述分类讨论的第 2 条删去即可。
5. 建图优化 - 优化边数
有一些题目,通常给你
如果直接建图,复杂度会炸掉。此时一般有两种思路:
- 使用数据结构维护。如线段树优化建图。
- 将图中冗余的边删去。即只用较少的边来等效地建出原图的很多边,常结合加点思想。
其中第 2 项是本文说明的重点。下面有 3 个例题,希望对以后做题有一定帮助。
例题 5.1
给定带边权的无向图,再给定常数,让你对每对点 连边权为 的边,求单源最短路。
如何建图?
。
显然只需在每一对
连边复杂度为
例题 5.2
给定带边权的无向图,再给定常数,让你对每对点 连边权为 的边,求单源最短路。
如何建图?(表示按位异或)
。
模仿例题 5.1 的做法。只需枚举每个点
连边复杂度为
例题 5.3
给定带边权的无向图,再给定常数,让你对每对点 连边权为 的边,求单源最短路。
如何建图?(表示按位或)
。
仿照例题 5.2 的做法,显然是行不通的。
考虑原图中一条从
由于
- 对每个点
,连边 , 。 - 对每个点
,枚举每个二进制位 (由低到高,从 0 开始编号)。- 若
的第 位为 ,连边 。 - 若
的第 位为 ,连边 。
- 若
具体而言,可以参考下图(图中省略角标)。
连边复杂度为
6. 次小生成树
有很多算法可以求最小生成树:Prim、Kruskal、Boruvka……但我们要求次小生成树。
由定义,次小生成树一定与最小生成树不同。
考虑用 Kruskal 算法求出一个最小生成树的边集
定理 6.1
存在一个次小生成树的边集,使得 与 只有一条边不同。
对非严格次小生成树,考虑所有在
由 Kruskal 算法的正确性证明,对于每一个环,
因此如果
对严格次小生成树,考虑在上述过程出现的环中,一定存在一条
因此如果
综上,无论要求非严格次小生成树还是严格次小生成树,定理 6.1 均成立。
有了定理 6.1,次小生成树就好求了。
对非严格次小生成树,首先用 Kruskal 算法求出最小生成树(边权和为
对严格次小生成树,在上述算法中,将用 LCA 求最大值的操作改成最大值和次大值一起求,边权记为
具体实现可以参考例题:洛谷 P4180 [BJWC2010] 严格次小生成树。
7. 综合应用
例题 7.1 (洛谷 P4899 [IOI2018] werewolf 狼人)
给定个点、 条边的无向连通图,和 次询问。
每次询问给定四个点、 、 和 ,你需要判断是否存在一条路径 ,使得 上的每一个点(包括端点)编号均在 内, 上的每一个点(包括端点)编号均在 内。
, , , 。
考虑 Kruskal 算法的流程:若当前枚举到的树边为
因此考虑构建 Kruskal 重构树。由上面的分析,若要求边权不超过
考虑原问题,显然可以构建 Kruskal 重构树。
对于
对于
则问题转换为在两颗 Kruskal 重构树的某两个子树中,点集是否有交集。
考虑对每颗重构树求 dfs 序,这样保证了每颗子树的节点编号连续。
将每个结点映射到平面直角坐标系中,横、纵坐标分别对应两颗树上的 dfs 序,则问题转化为二维数点问题。
用树状数组离线处理即可。时间复杂度为
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具