资讯详情

2024 CSP-S第二轮四题复盘:排序贪心、区间二分、线性DP与树形递推

📅 2026/10/29 21:29:52 | 华诺云谱 👁 阅读
2024 CSP-S第二轮四题复盘:排序贪心、区间二分、线性DP与树形递推
简介这份PDF资料收录了2024年CCF非专业级软件能力认证CSP-J/S第二轮提高级的上机真题面向备战信息学奥赛的中学选手与竞赛教练可用于赛前模拟、真题复盘与算法专项训练。资源包内共1个PDF文件约210KB完整呈现决斗、超速检测、染色、擂台游戏四道传统型题目的题面、输入输出格式、样例与数据范围并附有文件命名、main函数返回值、源文件100KB限制、栈空间与评测机配置等注意事项便于读者还原真实考场约束。目前已有5867人学习下载热度较高。读者可借助真题梳理拓扑排序、图着色、模拟与搜索等考点对照测试点分布与时限要求检验算法效率并据此制定针对性训练计划适合需要系统刷题与查漏补缺的竞赛选手。1. 四道题就是一张图2024年CSP-S第二轮真题到底在考什么很多人拿到这套题的第一反应是“四道题四个方向刷完就忘”。但把四道题摆在一起看会发现它们其实共用同一张知识地图排序与贪心构造、区间与二分、动态规划的状态设计、树形结构的模拟与计数。决斗考的是“排序后怎么贪心配对”超速检测考的是“把物理过程翻译成区间再二分”染色考的是“相邻同色约束下的线性DP”擂台游戏考的是“完全二叉树上的胜者集合递推”。这四块拼起来正好覆盖了提高级第二轮最常出现的四类思维模型。如果你正在系统复习信息学奥赛或者刚考完想复盘这份梳理适合你它不按题号讲而是按“知识模块—重点难点—易错点—复习清单”的顺序把四道题拆成可复用的模板。下面从每道题的核心模型开始一层层往下拆。2. 决斗排序贪心与配对策略的复习重点2.1 题目模型每只怪兽只能攻击一次怎么让存活数最少决斗的规则可以压缩成一句话每只怪兽有一个攻击力等于防御力的值 r一只怪兽攻击另一只时若攻击力严格大于对方防御力对方出局每只怪兽整场最多攻击一次当所有存活怪兽都攻击过游戏结束。目标是让最终存活的怪兽数量最少。这个模型的关键在于“每只怪兽最多攻击一次”和“攻击力等于防御力”两个条件。前者意味着攻击是一种稀缺资源后者意味着强弱关系完全由 r 的大小决定。把 r 排序后问题就变成了用哪些怪兽去“吃掉”哪些怪兽才能让剩下的最少。2.2 贪心构造排序后从大到小配对梳理下来最优策略是将 r 从小到大排序用较大的怪兽去攻击较小的怪兽。更具体地说从最大的怪兽开始尽量让它吃掉一个比它小的、尚未被吃掉的怪兽如果找不到可吃的它就存活。这样做的直觉是大怪兽的攻击力足以吃掉小怪兽而小怪兽之间的攻击往往因为“小于等于防御力则无事发生”而浪费攻击机会。用代码表达这个贪心框架大致如下// 决斗核心贪心框架示意 sort(r 1, r n 1); // 从小到大排序 int alive n; // 初始全部存活 int j n; // 从最大的开始尝试攻击 for (int i n; i 1; --i) { // 尝试让第 i 只怪兽吃掉一个比它小的存活怪兽 // 实际实现中通常用双指针或计数维护 }参数说明r数组存每只怪兽的攻防值n为怪兽数量。排序后贪心的正确性依赖于“大能吃小、小不能吃大”的单调性。这里最容易翻车的地方是不能简单地认为“排序后相邻配对”就是最优因为一只大怪兽可能吃掉多个小怪兽中的某一个而剩下的配对关系会连锁变化。正确的做法是维护一个“可被吃掉的候选集合”从大到小扫描能配对就配对。2.3 特殊性质A随机数据下的期望分析题目给了一个特殊性质 A每个 r 在值域中独立均匀随机生成。这个性质不是让你写随机算法而是提示你在随机数据下很多怪兽的值会集中在值域中段贪心的配对成功率有期望上的规律。复习时要注意特殊性质往往是出题人给的“部分分提示”——如果你不会正解可以针对随机数据写一个基于期望的近似或暴力验证。但正解仍然要回到排序贪心的正确性证明上。提示决斗的贪心证明核心是交换论证——如果存在一个最优方案中某只大怪兽没有吃掉它能吃掉的小怪兽把这次攻击换给它不会使存活数变多。3. 超速检测物理过程转区间与二分查找的难点3.1 从运动学公式到超速区间超速检测的物理部分并不复杂但把物理翻译成代码是第一个坎。题目给了匀加速运动的公式位移 s 时瞬时速度满足 v² v0² 2as。一辆车从位置 d 驶入初速度 v0加速度 a道路限速 V。要判断它在哪些位置超速本质是解一个不等式若 a 0速度恒为 v0全程要么超速要么不超速。若 a 0速度递增存在一个临界位移 s*之后速度超过 V。若 a 0速度递减存在一个临界位移 s*之前速度超过 V。把临界位移换算成道路上的位置区间就得到了这辆车“超速的位置区间”。这一步的难点在于边界处理车辆驶入和驶出位置如果正好有测速仪也要算。很多人的第一版代码就是在这里漏掉了端点。3.2 第一问所有测速仪开启时的超速车辆计数第一问相对直接对每辆车求出超速区间然后判断这个区间内是否存在至少一个测速仪位置。由于测速仪位置 p 是递增给出的可以用二分查找快速判断。梳理下来每辆车的处理是 O(log m)总复杂度 O(n log m)在 n, m ≤ 10⁵ 下完全够用。// 判断车辆超速区间 [l, r] 内是否有测速仪p 已排序 int idx lower_bound(p 1, p m 1, l) - p; if (idx m p[idx] r) { // 该车会被判定为超速 }参数说明l和r是超速区间的左右端点换算到道路位置p是测速仪位置数组。lower_bound找到第一个不小于 l 的测速仪再判断它是否不超过 r。这里要注意浮点数精度题目明确提示“如果使用浮点数需要注意精度问题”稳妥的做法是把不等式两边平方用整数运算避免开方。3.3 第二问最多关闭多少测速仪——区间覆盖的贪心第二问是整套题里思维量较大的一问。要求是关闭尽量多的测速仪但保证所有原本会被判定超速的车仍然被判定超速。换句话说每辆超速车对应的超速区间内至少要保留一个测速仪。这就变成了一个区间选点问题给定若干区间每辆超速车的超速区间选择尽量少的点测速仪使得每个区间至少包含一个点。最多关闭数 m − 最少保留数。区间选点的经典贪心是按区间右端点排序每次选择当前区间右端点之前最靠右的可用测速仪。但本题的测速仪位置是给定的离散点所以实现时要结合二分查找定位。// 区间选点贪心示意按右端点排序后依次处理 sort(seg 1, seg cnt 1, cmp_by_right); int keep 0, last -1; for (int i 1; i cnt; i) { if (last seg[i].l) { // 当前区间还没有被覆盖 // 在 [seg[i].l, seg[i].r] 中选最靠右的测速仪 int pos upper_bound(p 1, p m 1, seg[i].r) - p - 1; last p[pos]; keep; } }参数说明seg存每辆超速车的超速区间cnt是超速车数量p是测速仪位置。last记录上一个被选中的测速仪位置。核心逻辑是如果当前区间的左端点已经大于上一个选点说明这个区间没被覆盖必须新选一个点且选在右端点以内最靠右的位置这样对后面的区间最有利。注意第二问只统计“原本会被判定超速”的车第一问没被判超速的车不产生约束。这个条件如果读漏会把区间集合搞错。4. 染色相邻同色约束下的线性DP设计4.1 得分规则的重新表述染色题的定义读起来绕但梳理下来可以这样理解每个位置染红或蓝对于位置 i看它左边最近的同色位置 j如果 A[i] A[j]则贡献 A[i]否则贡献 0。目标是最大化总贡献。关键观察是贡献只发生在“同色且值相等”的最近同色对之间。也就是说如果你把相同值的两个位置染成同色并且它们之间没有同色的其他位置就能拿到这个值的贡献。这天然适合用动态规划按位置推进。4.2 状态设计以颜色为维度的线性DP一个可行的状态设计是dp[i][c]表示处理到第 i 个位置、第 i 个位置染颜色 c 时的最大得分。转移时需要知道“左边最近的同色位置是哪个、值是多少”。直接枚举会退化成 O(n²)需要优化。优化思路是对于每个颜色 c维护“上一个染成颜色 c 的位置的值”。当处理位置 i 时如果把它染成颜色 c那么它左边最近的同色位置就是上一个染 c 的位置。如果那个位置的值等于 A[i]就额外获得 A[i] 的贡献。因此转移只依赖两个颜色各自的上一个位置信息。// 染色 DP 状态转移示意 // dp[c] 表示当前处理到的位置染成颜色 c 时的最大得分 // lastVal[c] 表示颜色 c 上一个位置的值 for (int i 1; i n; i) { for (int c 0; c 2; c) { int add (lastVal[c] A[i]) ? A[i] : 0; newDp[c] max(dp[0], dp[1]) add; // 从任意前一颜色转移 } // 更新 lastVal 和 dp }参数说明dp[c]是当前颜色 c 的最大得分lastVal[c]是该颜色最近位置的值。转移时位置 i 染成颜色 c 的得分等于“前一个位置任意颜色的最大得分”加上“如果同色最近值相等则加 A[i]”。这里要注意lastVal的更新时机是在转移之后且只更新当前选择的颜色。4.3 多组数据与值域处理染色题有 T 组数据n 最大 2×10⁵A[i] 最大 10⁶。复习时要留意不要对每组数据都开一个 10⁶ 的数组做清空否则 T 较大时会超时。正确做法是用到哪些值就记录哪些值或者用时间戳标记。这个细节在考场上很容易被忽略属于“写对了转移但死在初始化”的典型翻车点。5. 擂台游戏完全二叉树模拟与胜者集合递推5.1 比赛结构2的幂次人数与轮次规则擂台游戏的比赛结构是一棵完全二叉树2^k 名选手第 R 轮的第 G 场比赛抽签决定擂主是编号小还是编号大的一方擂主能力值 ≥ R 则擂主获胜否则挑战者获胜。注意胜负只取决于擂主的能力值和轮次 R与另一方的能力值无关。这个规则很反直觉但正是它让问题可做每场比赛的胜者只由擂主和轮次决定。5.2 询问处理前缀人数与补充选手题目给了 m 个询问每个询问 c 表示只收到前 c 位选手需要补充到 2 的幂次补充选手能力值任取问所有可能成为总冠军的选手编号之和。这里的关键是补充选手的能力值可以任取所以如果补充选手有可能获胜也要计入答案。这意味着对于每个前缀我们要判断哪些位置“存在一种能力值赋值使得它夺冠”。梳理下来这需要从叶子往上递推每棵子树“可能胜出的选手集合”。由于补充选手能力值自由某些内部节点上的胜者集合会扩大。特殊性质 B所有 d 0提示了简化情形擂主总是编号小的一方胜负只由能力值和轮次比较决定这时递推会退化成简单的阈值判断。5.3 异或输出与多组数据的处理最终输出不是每个询问的答案而是所有询问答案按(i × A_i)异或起来的结果。这个输出方式要求你把每个询问都算对错一个就全错。复习时要注意异或运算不满足结合律的“可撤销”直觉不能中途偷懒。另外 T 组数据通过a_i a_i ⊕ X_{i mod 4}生成说明同一套结构要跑多次预处理比赛树结构可以复用。注意询问的 c_i 可能不是 2 的幂次补充人数 2^k − c_i其中 k 是使 2^k ≥ c_i 的最小非负整数。这个 k 对每个询问单独计算不能直接用全局的 K。6. 易错点清单这四道题最容易翻车的地方6.1 易漏点一决斗中把“攻击力等于防御力”当成严格大于决斗的规则是“攻击力小于等于防御力则无事发生”也就是说必须严格大于才能击破。很多人在排序贪心时写成导致本应存活的怪兽被误判出局。为什么容易漏因为日常直觉里“打平”往往算有效但题目明确是“小于等于则无事发生”。补法很简单在比较函数和判断条件里统一用严格大于写完用样例 1 的1 2 3 1 2手算一遍验证。6.2 易漏点二超速检测的端点测速仪题目特意强调“当车辆驶入与驶出主干道时如果在对应位置有一个开启的测速仪这个测速仪也会对这辆车进行测速”。这意味着超速区间的端点如果是闭区间就要包含驶入和驶出位置。为什么容易漏因为用浮点数算临界位置时端点往往被当成开区间处理。补法把所有不等式平方后用整数比较并且显式判断 d 和 L 位置是否有测速仪。6.3 易漏点三染色DP的初始化清空代价染色题 T 组数据如果每组都 memset 一个 10⁶ 的数组T10 时就是 10⁷ 次操作看似不多但配合其他操作可能卡时限。更隐蔽的是如果lastVal没有正确重置上一组数据的状态会污染下一组。为什么容易漏因为转移逻辑写对了注意力全在 DP 上。补法用vector按需分配或者用“版本号”数组标记当前数据组避免全量清空。6.4 易漏点四擂台游戏的补充选手计入答案擂台游戏明确说“如果补充的选手有可能获胜也应当计入答案中”。很多人只统计原始 n 名选手漏掉了补充位置。为什么容易漏因为补充选手没有具体能力值直觉上觉得“不存在的人不算”。补法在递推胜者集合时把补充位置视为“能力值可任意取”的自由节点它的胜者集合要按“存在性”展开而不是按具体值判断。6.5 易漏点五文件命名与编译选项的硬性要求四道题都要求源文件名为duel.cpp、detect.cpp、color.cpp、arena.cpp输入输出文件名对应.in/.out编译选项-O2 -stdc14 -static。这些不是“建议”是硬性要求违反直接不予受理。为什么容易漏因为本地调试时用标准输入输出很方便到了提交才改一紧张就改漏。补法平时练习就按正式文件名建文件把freopen写成固定模板考前默写一遍。7. 复习清单从四道题反推的查漏补缺路线把四道题拆完之后复习路线其实很清晰。下面这张表是我自己复盘时用的对照表按“模型—对应题目—需要练到什么程度”三列整理知识模块对应题目复习达标标准排序 贪心 交换论证决斗能独立写出贪心并口述正确性证明运动学公式转不等式 二分超速检测第一问能处理 a0、a0、a0 三种情况且端点不漏区间选点贪心超速检测第二问能区分“覆盖所有区间”和“覆盖超速车区间”线性DP 状态压缩染色能写出 O(n) 转移并解释 lastVal 的作用完全二叉树模拟 集合递推擂台游戏能处理补充选手的“存在性”展开文件规范与编译选项全部四题能默写 freopen 模板和编译命令具体怎么用这张表查漏补缺我的做法是先不看题解把每道题的核心模型用一句话写出来然后对照上表看自己卡在哪一列。如果“排序贪心”写不出交换论证就回去补贪心证明的套路如果“区间选点”分不清两种覆盖就把超速检测第二问的区间集合重新推一遍确认只统计第一问判超速的车。擂台游戏的递推如果推不动先从特殊性质 B所有 d0入手把阈值判断写对再扩展到一般情况。还有一个容易被忽略的复习动作把四道题的文件名和输入输出格式集中默写一遍。听起来很笨但每年都有因为文件名写错或freopen漏改而爆零的情况。我自己的习惯是考前一周每天花五分钟在白纸上默写四组freopen(xxx.in, r, stdin)和对应的.out写到不用想就能写出来为止。最后说一个血泪教训超速检测那道题我第一版用浮点数算临界位置样例过了但自己造了一组 a0 且临界点正好落在测速仪上的数据就翻车了。从那以后我每次遇到“物理公式转代码”的题都强制走一遍“平方去浮点 端点单独判断”的流程不再相信样例的侥幸。希望这份梳理能帮你在复习时少走一遍我走过的弯路把四道题真正吃成四个可复用的模板。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑