1. 图的遍历算法解析与洛谷3916题实战
(开篇以开发者视角直接切入主题)
最近在洛谷刷题时遇到一道经典的图遍历问题(P3916),发现很多朋友对图的遍历算法理解不够深入,导致解题效率低下。今天我就结合这道题目,分享一下图的深度优先搜索(DFS)和广度优先搜索(BFS)的底层原理、实现差异和实战技巧。这道题虽然标为"简单",但其中蕴含着几个值得注意的优化点,我在初次提交时也踩过坑。
图遍历是算法竞赛中的基础技能,在社交网络分析、路径规划、依赖解析等场景都有广泛应用。洛谷3916题要求找出每个顶点能到达的最大编号顶点,看似简单实则考察了对遍历算法的深入理解。下面我会从图的存储方式选择开始,逐步拆解解题思路,最后给出经过多次优化后的AC代码。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 题目分析与数据结构选择
2.1 洛谷P3916题目重述
题目给定一个有向图,包含N个顶点(1≤N≤1e5)和M条边(1≤M≤1e5)。对于每个顶点v,要求输出从v出发能够到达的最大顶点编号。这个问题的关键在于高效处理大规模图的遍历,常规的暴力解法会导致O(N^2)时间复杂度,在1e5数据量下必然超时。
2.2 邻接表与邻接矩阵的抉择
面对图问题,首先需要选择合适的数据结构:
- 邻接矩阵:O(1)查询但需要O(N^2)空间,本题N=1e5时内存直接爆炸
- 邻接表:空间O(N+M),更适合稀疏图
cpp复制// 邻接表标准实现
vector<vector<int>> adj(N+1); // 顶点编号1~N
while(M--){
int u, v;
cin >> u >> v;
adj[u].push_back(v);
}
注意:有向图只需单向存储边,但若题目改为无向图则需双向存储
2.3 逆向思维优化
直接对每个顶点做完整遍历显然不现实。观察到:
- 最大编号顶点只能到达自己
- 次大编号顶点要么到达自己,要么通过边到达最大编号顶点
- 可以逆向处理,从大到小遍历顶点
这种思路将时间复杂度从O(N^2)降为O(N+M),是典型的"逆向思维+记忆化"优化。
3. 深度优先搜索(DFS)实现详解
3.1 基础DFS实现
cpp复制vector<int> max_node(N+1, 0);
v
