资讯动态

PTA天梯赛L2-016题保姆级攻略:用DFS搞定‘五服禁婚’判断(附C++完整代码)

发布时间:2026/9/14 18:01:45 来源:尧图企业网站定制
PTA天梯赛L2-016题深度解析用DFS实现家族关系高效判定当算法竞赛遇到中国传统的五服制度会碰撞出怎样的火花这道PTA天梯赛L2-016题将家族关系判断与图遍历算法巧妙结合成为检验选手实际问题建模能力的经典案例。作为竞赛选手我们需要在理解传统文化规则的基础上将其转化为计算机可执行的判定逻辑。1. 问题本质与算法选择题目要求判断两个人是否在五服之内即是否存在五代以内的共同祖先这本质上是一个家族关系图的连通性判断问题。我们需要考虑几个关键特征数据规模最多10^4个人物节点要求算法时间复杂度控制在O(N)级别查询特性需要频繁判断两个人的亲属关系最多10^4次查询遍历深度只需要考虑五代以内的亲属关系**DFS深度优先搜索**成为最合适的选择原因在于实现简单直观适合竞赛环境快速编码五代限制天然对应DFS的深度参数不需要完整遍历整个家族图遇到深度限制即可返回与BFS相比DFS在这种特定深度限制的场景下更具优势。我们来看一个简单的亲属关系示例A ├── B │ ├── D │ └── E └── C ├── F └── G当判断B和C的后代关系时DFS会沿着每条分支深入直到达到五代限制。2. 数据结构设计与预处理高效的数据结构是算法实现的基础。针对本题我们需要设计能够快速查询以下信息的数据结构每个人的性别信息每个人的父母信息每个人的后代信息反向索引2.1 核心数据结构实现const int MAX_ID 100000; // 题目说明ID为5位数 char gender[MAX_ID]; // 性别记录数组 vectorint parents[MAX_ID]; // 父母关系图 setint ancestors[2]; // 两个人的五代祖先集合关键预处理步骤读取每个人物信息时同时记录其父母的性别建立从子女到父母的关系指针特别注意处理父母信息缺失的情况ID为-1注意题目虽然保证输入数据中每人只有一个性别但在实际处理时仍需考虑父母性别记录的完整性这是许多选手容易忽略的边界条件。3. DFS实现与优化技巧标准的DFS实现需要针对本题特点进行优化。以下是竞赛中经过验证的高效实现方案3.1 基础DFS框架void dfs(int current, int depth, int person_index) { if(depth 5) return; ancestors[person_index].insert(current); for(int parent : parents[current]) { if(parent ! -1) { dfs(parent, depth 1, person_index); } } }3.2 竞赛实用优化技巧提前终止当发现共同祖先时立即返回不必继续完整遍历双集合比对先收集一个人的所有五代亲属再检查另一个人的亲属是否存在于该集合性别优先判断在DFS前先判断两人性别同性可直接返回结果优化后的查询逻辑bool canMarry(int a, int b) { if(gender[a] gender[b]) return false; ancestors[0].clear(); ancestors[1].clear(); dfs(a, 1, 0); dfs(b, 1, 1); for(int relative : ancestors[1]) { if(ancestors[0].count(relative)) { return false; } } return true; }4. 常见错误与调试技巧在竞赛环境中这道题有几个典型的坑点需要特别注意4.1 易错点列表父母性别记录遗漏只记录了输入人物的性别而忽略了父母性别解决方案在输入时显式设置父母性别ID边界处理不当未考虑ID为-1的情况直接访问数组导致越界解决方案在访问前检查parent ! -1五代计算错误将五代理解为五层而非五代本人为第一代正确理解本人(1)→父母(2)→祖父母(3)→曾祖父母(4)→高祖父母(5)集合未清空多次查询间未清空祖先集合导致结果污染解决方案每次查询前清空集合4.2 调试技巧当遇到WAWrong Answer时可以构造以下测试用例进行验证测试案例预期结果检查重点同性查询Never Mind性别判断逻辑直系血亲No父母关系处理远房表亲Yes五代边界判断父母信息缺失根据情况-1处理逻辑5. 完整竞赛级代码实现结合上述所有优化和注意事项以下是适合竞赛环境的高效实现#include bits/stdc.h using namespace std; const int MAX_ID 100000; char gender[MAX_ID]; vectorint parents[MAX_ID]; setint ancestors[2]; void dfs(int u, int depth, int idx) { if(depth 5 || u -1) return; ancestors[idx].insert(u); for(int parent : parents[u]) { dfs(parent, depth 1, idx); } } void solve() { int n, k; cin n; while(n--) { int id, father, mother; char g; cin id g father mother; gender[id] g; if(father ! -1) { gender[father] M; parents[id].push_back(father); } if(mother ! -1) { gender[mother] F; parents[id].push_back(mother); } } cin k; while(k--) { int a, b; cin a b; if(gender[a] gender[b]) { cout Never Mind endl; continue; } ancestors[0].clear(); ancestors[1].clear(); dfs(a, 1, 0); dfs(b, 1, 1); bool hasCommon false; for(int rel : ancestors[1]) { if(ancestors[0].count(rel)) { hasCommon true; break; } } cout (hasCommon ? No : Yes) endl; } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); solve(); return 0; }这段代码经过了PTA平台的实际验证包含了所有必要的优化和边界处理。在竞赛环境中建议使用更简洁的变量名以加快编码速度但核心逻辑保持不变。6. 算法扩展与变式思考虽然DFS是本题的最直接解法但我们可以进一步思考其他可能的解决方案和优化空间6.1 替代算法对比算法优点缺点适用场景DFS实现简单深度控制自然重复计算多查询次数少时BFS层次遍历直观需要额外记录层数需要广度优先时并查集查询速度快难以处理五代限制不适用本题预处理所有关系查询O(1)预处理开销大查询极多时6.2 性能优化方向对于更大规模的数据可以考虑以下优化记忆化存储缓存每个人的五代祖先集合双向搜索同时从两个人出发向祖先搜索迭代式DFS避免递归深度限制7. 竞赛实战建议在真实的竞赛环境中处理这类题目时建议遵循以下步骤仔细阅读题目特别注意关于数据范围和特殊条件的说明设计数据结构选择最适合题目要求的数据存储方式编写伪代码先理清算法流程再着手编码处理边界条件特别是输入中的-1等特殊值构造测试用例包括极端情况和典型情况这道L2-016题很好地展示了如何将传统文化规则转化为算法问题。掌握这种转换能力是成为高水平竞赛选手的关键。在实际编码时保持代码模块化和清晰的结构有助于快速调试和修改。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价