01 / 本课学习路线
本课学习路线
汇总与分类 25 分钟 · 订正案例 25 分钟 · 修复与独立重写 65 分钟 · 收尾 15 分钟
02 / 订正流程与完成条件
订正不是重做一遍:四个动作,每道错题各走一次
「再做一遍」不是修复。订正的定义是:说清第一次错在哪一类、用一个最小用例复现它、改掉它、证明改对了、再不看旧代码重写一次。前四个模块的错题订正已经走过四遍,本课把同一套流程用在本模块的贪心、二分、树与规则模拟题上。
| 动作 | 做什么 | 产出 | 对应正文 |
|---|---|---|---|
| ① 分类 | 按第一次出错的原因归到六类之一 | 登记表的「首次错误类型」一栏 | 第 03 节 |
| ② 定位并修复 | 找到导致错误的那几行代码或那条被漏读的规则,改掉 | 登记表的「错误原因」「修复动作」 | 第 03、04 节 |
| ③ 最小验证 | 构造一个只触发该错误的输入,修复前失败、修复后通过 | 登记表的「触发用例」「最小验证」 | 第 04、05 节 |
| ④ 独立重写 | 不看旧代码从空文件重写整题,用复测清单验证后提交 | 登记表的「独立重写」 | 第 06 节 |
本课完成前需要完成四项检查:按六类记录错误原因,重新学习最薄弱的一类,独立重写 P3130 与 P2488 并记录用时,再更新考前检查清单。这两题用于复习,但独立重写仍是第 3 项检查的要求。尚未完成的项目保持未勾选;有未解决的问题可以先记录并继续学习,之后回来补完。先用触发原错误的输入检查修改,再验证边界和题面样例,最后独立重写并提交。
| 来源 | 题号 | 判题结果 | 先猜的错误类型 | 订正状态 |
|---|---|---|---|---|
| 阶段测验第 1 题 | P3508 | 答案错误(WA) / 运行错误(RE) / 未完成 / 通过(AC) | — | 未开始 |
| 阶段测验第 2 题 | P3408 | 同上 | — | 未开始 |
| 阶段测验第 3 题 | P3117 | 同上 | — | 未开始 |
| 第 1~5 课 未通过或看提示才通过的必做题 / 进阶题 | 逐题填 | 填判题结果 | — | 未开始 |
「先猜的错误类型」在第 03 节按判定信号确认后再改;AC 的题只登记曾经改过的具体问题,不进入订正流程。看提示才通过的题按「未通过」处理——提示告诉你的那一步,就是要独立重写验证的那一步。
03 / 六类错误的判定与定位
判定信号、定位步骤、修复动作、最小用例怎么构造(本模块版)
分类不靠感觉,靠信号:先看判题结果是哪一种,再用下面的问题逐个排除。本模块的题多了「策略对不对」这一层——排序键、撤销的键、中点方向、返回值分工——它们的错误几乎都落在建模一类;等号、只剩一人、空区域落在边界一类。
| 类型 | 判定信号 | 定位步骤 | 修复动作 | 最小用例怎么构造 |
|---|---|---|---|---|
| 读题 | 样例能过、某些用例 WA;对「至少 15 分钟」「按期 = 不晚于截止」「并列取编号小」的理解与题目页不一致 | 重新写题目要求清单:比较是否含等号、附加规则、并列规则;逐条对照代码 | 按那一条改一处 | 构造恰好落在等号上的输入(案例 A:完成时刻恰好等于截止时刻) |
| 建模 | 所有用例都错或大面积错;说不清排序键、堆里存什么、中点往哪偏、返回值是什么 | 写出策略一句话,用课里的反例检验(第 1 课 的三场演出、第 2 课 的单物品、第 3 课 的 lo=5 hi=6、第 4 课 的三个问题) | 换排序键 / 换撤销的键 / 成对改中点与收缩 / 改返回值分工 | 取课里现成的反例(案例 B:第 1 课 第 04 节的三场演出) |
| 边界 | 只有特定输入错:只剩一人、n = 1、单场演出、空堆、开局冷却 | 列出四类边界输入逐个手算 | 补上判断(<= 循环、空堆先判、n = 1 直接输出) | 就是那个边界输入本身(案例 C:只剩一人) |
| 复杂度 | 超时(TLE),且不是读入问题 | 估算最坏次数:每步线性找最大、判定函数里排序、出队才标记 | 换堆 / O(n) 判定 / 入队即标记 | 构造最大规模输入计时 |
| 语言 | 运行错误(RE)或结果对不上却找不到逻辑错 | 写 3 行小实验:heappop 空堆、split(",")、递归上限、[[]] * n 别名 | 换成明确的写法 | 触发该语言行为的最短输入 |
| 输出格式 | 答案错误(WA)或格式错误(PE),数字都对 | 把自己的输出与期望输出逐字节对比 | 改分隔符、行数、提前结束不补空行 | 题面示例 |
登记时的一条硬要求:「错误原因」必须具体到能据此直接改代码——「粗心」「没注意」不符合要求,「循环条件写成 l < r,l == r 时最后一个人没有上船」符合。写不出具体原因,说明还没定位到,回到定位步骤。
04 / 三个完整订正案例
失败用例 → 定位 → 修改前后对照 → 复测
三个案例都来自本模块第 1、2 课 必做题里真实会出现的错误,每个都走完整流程;你的错题按同样的格式处理。
案例 A · 读题类:AI021 超期判断写成了 >=
判题结果: 答案错误(WA);题面示例 1 就不过(第一个任务耗时 2、截止 2,恰好卡在等号上) 失败用例: 1 / 5 5 期望 1 实际 0 定位: 重写题目要求清单 → 「按期 = 完成时刻不晚于截止时刻」,等号成立算按期(第 2 课 第 04 节的表里 2 ≤ 2 算接下);代码写成 used >= d,恰好在截止时刻完成的任务被当成超期撤销 修复: 比较改成严格大于(见下方前后对照) 复测: 1 / 5 5 → 1 ✓ 2 / 2 3 / 1 6 → 2 ✓(没有等号情形,两种写法结果相同) 题面示例 1(3 / 2 2 / 3 5 / 2 4)→ 2 ✓
案例 A 修改前(片段)
Python# AI021 修改前:超期判断写成了 >=——恰好在截止时刻完成的任务被当成超期撤销
for d, t in tasks:
heapq.heappush(heap, -t) # 先接下
used += t
if used >= d:
used += heapq.heappop(heap)案例 A 修改后(片段)
Python# AI021 修改后:题面「按期」含等号 → 只有严格超过截止才撤销
for d, t in tasks:
heapq.heappush(heap, -t) # 先接下
used += t
if used > d: # 超期:撤销已接任务里耗时最大的一个
used += heapq.heappop(heap) # 弹出的是 -t_max,加上它等于减去 t_max只改了比较符;完整程序在第 2 课 第 07 节展开区。修改前后各跑一次三个复测用例,修改前第一个用例失败、修改后全部通过——这才是「修复生效」。
案例 B · 建模类:P3110 按开始时间排序
判题结果: 答案错误(WA);开始顺序与结束顺序一致的用例能过 失败用例: 3 / 0 100 / 1 1 / 30 1 期望 2 实际 1 定位: 用第 1 课 第 04 节的三场演出反例检验:最长的一场开始得最早,按开始时间会先选上它,后面两场都被挡住——排序键选错是策略本身错,不是某个特例 修复: 排序键改成结束时间(见下方前后对照) 复测: 3 / 0 100 / 1 1 / 30 1 → 2 ✓ 4 / 600 60 / 630 60 / 700 60 / 775 60 → 3 ✓(开始顺序与结束顺序一致,两种排序结果相同) 3 / 0 20 / 35 20 / 22 10 → 2 ✓
案例 B 修改前(片段)
Python# P3110 修改前:排序键写成了开始时间——最早开始的长演出会挡住后面的短演出
shows.sort(key=lambda s: s[0])案例 B 修改后(片段)
Python# P3110 修改后:排序键是结束时间——结束得早,给后面留的空间最多
shows.sort(key=lambda s: s[1]) # 排序键:结束时间早的在前建模类错误的定位工具是课里现成的反例:三场演出一跑就知道方向。完整程序在第 1 课 第 08 节展开区。
案例 C · 边界类:P3118 循环条件写成 l < r,只剩一人时没上船
判题结果: 答案错误(WA);题面示例就不过(第 3 轮 l == r 时剩下的那个人没上船) 失败用例: 5 3 / 1 2 3 期望 2 实际 1 定位: 逐轮手推(第 1 课 第 06 节的双指针表):第 1 轮 1 + 3 ≤ 5 同船,l ← 1、r ← 1;第 2 轮 l == r,循环条件 l < r 不成立直接退出——体重 2 的人没有船 修复: 循环条件改成 l <= r(见下方前后对照) 复测: 5 3 / 1 2 3 → 2 ✓ 10 4 / 1 9 2 8 → 2 ✓(偶数人全部两两配对,不会出现 l == r) 题面示例 3 4 / 3 2 2 1 → 3 ✓
案例 C 修改前(片段)
Python# P3118 修改前:循环条件写成了 l < r——l == r 时最后一个人没有上船
while l < r:
if weight[l] + weight[r] <= limit:
l += 1
r -= 1
boats += 1案例 C 修改后(片段)
Python# P3118 修改后:l == r 时还剩一个人,也要一条船
while l <= r: # 每轮处理最重的 weight[r]
if weight[l] + weight[r] <= limit: # 最轻的能和最重的同船
l += 1
r -= 1 # 无论配没配上,最重的这个人都上船了
boats += 1边界类错误的最小用例就是那个边界输入本身:三个人就够(奇数人数必然剩一个)。完整程序在第 1 课 第 08 节展开区,逐轮双指针表在第 1 课 第 06 节。
05 / 错题登记与最小验证
登记模板、填好的示例、最小用例的三个要求
登记表是订正的账本:每道错题一条,订正完把「独立重写」一栏改成「通过」。下面先给空模板,再给案例 C 填好的样子。
错题登记模板(每题一条)
Python# 错题登记(每题一条;订正完把「独立重写」改成「通过」)
# 题号:
# 来源: 阶段测验第_题 / 课_ 必做 / 课_ 进阶
# 判题结果: WA / RE / TLE / PE
# 首次错误类型: 读题 / 建模 / 边界 / 复杂度 / 语言 / 输出格式
# 触发用例: 输入=____ 期望=____ 实际=____
# 错误原因: (具体到能据此直接改代码)
# 修复动作:
# 最小验证: (至少三条:触发错误的、边界另一侧的、题面示例)
# 独立重写: 未做 / 通过 / 再错(类型=__)复制进你的错题本,每订正一项填一条;如果暂时无法准确描述错误原因,请结合失败用例和代码位置进一步检查,再回到六类错误原因重新分类。
填好的示例(案例 C)
Python# 题号: P3118
# 来源: 第 1 课 必做
# 判题结果: WA
# 首次错误类型: 边界
# 触发用例: 输入=5 3 / 1 2 3 期望=2 实际=1
# 错误原因: 循环条件写成 l < r,l == r(只剩一个人)时直接退出,最后一个人没有上船
# 修复动作: 循环条件改成 l <= r;只剩一人时也要一条船
# 最小验证: 5 3 / 1 2 3 → 2 ✓ 10 4 / 1 9 2 8 → 2 ✓(偶数人全部配对,不会出现 l == r) 3 4 / 3 2 2 1 → 3 ✓(题面示例)
# 独立重写: 通过「触发用例」写明期望与实际;「错误原因」写到能直接改代码;「最小验证」至少三条:触发错误的、边界另一侧的、题面示例。
| 要求 | 含义 | 反例 |
|---|---|---|
| 只触发这一类错误 | 修复前失败、修复后通过,且不涉及其它规则 | 拿 1000 场演出验证「排序键」 |
| 尽量短 | 手算能在 1 分钟内得到期望输出 | 用 10⁵ 个任务验证「等号取不取」 |
| 带期望输出 | 登记时写清期望与实际 | 只写「输入 5 3 / 1 2 3」不写期望 |
每订正一项做一次最小验证:不要直接重交原题,先跑最小用例确认修复真的生效,再独立重写整题。本模块每课第 09 节的错误表都是现成的最小用例来源。
06 / 独立重写与复测清单
不看旧代码从空文件重写,用清单验证后再提交
独立重写检验的是「离开旧代码还能不能写对」。重写前只允许看题目页和自己的策略一句话注释,不看旧代码、不看参考程序。
| 题目 | 题面示例 | 边界用例 | 错误专项用例 |
|---|---|---|---|
| P3130 最大报酬 | 本课程自算 3 4 / 1 5 / 1 3 / 2 4 / 3 2 → 11 | 1 4 / 1 5 / 1 3 / 2 4 / 3 2 → 5(只能做一项) | 2 3 / 5 10 / 5 9 / 5 8 → 19(漏掉 min(k, T) 会得 27) |
| P2488 中文分词模拟器 | ilovechina + 词库 → i,love,china;iat → i,a,t | ilovechina,thewordisbeautiful → 标点只断句 | 含 ilove 的词库 → ilove,china(最短匹配会得 8 段) |
| P3508 战场索敌 | 题面示例(题目页给出输入与输出) | 2 2 1 / ## / ##(全是障碍);1 3 1 / ...(没有敌人) | 1 5 2 / E..E.(只有一行)——期望输出以交卷后的测验解析为准 |
| P3408 工作安排 | 题面示例(题目页给出输入与输出) | T 小于所有耗时 | 10 3 / 6 7 / 5 5 / 5 5(报酬最高的一项占掉大半时间)——期望输出以交卷后的测验解析为准 |
| P3117 派出团队 | 题面示例(题目页给出输入与输出) | 1 / 7 / 12(只有一人);3 / 8 9 10 / 8 | 4 / 9 9 1 1 / 10(两强两弱)——期望输出以交卷后的测验解析为准 |
清单里给出的输出与各课正文的推演一致;阶段测验三题的期望输出以交卷后的测验解析为准。重写后先跑清单再提交;提交通过后把登记表的「独立重写」改成「通过」。
重写仍未通过怎么办:对比两次失败用例和代码位置——如果是同一个用例失败,说明错误原因没定位准,回到第 03 节重新分类;如果是新的用例失败,登记一条新的错题。两次都不过也不算失败,登记表里写明具体问题,带着它进下一模块。
07 / 迁移练习与参考答案
分类练习、找错练习、三套写法默写,以及没有错题的同学做什么
每题先自己做,再展开答案。
练习 1(分类):下面五个失败描述各属于六类中的哪一类?① P3110 输出比正确答案多 1,输入 2 / 0 60 / 70 60;② AI021 在「一个 6 换四个 1」上输出 2(期望 4);③ P2497 程序不结束;④ P3904 抛出 RecursionError(孩子下标写成 2i / 2i+1);⑤ AI042 输出比期望多两行空行。
展开练习 1 答案
① 读题(漏掉 15 分钟转场);② 建模(只跳过不撤销);③ 建模(中点与收缩方向只改一半——策略写法本身错);④ 语言(孩子下标算错:根 0 的左孩子仍是 0,自己调用自己直到超过递归上限);⑤ 输出格式(提前结束后补了空轮)。判定依据:①只有含转场的用例错;②③方法本身错;④运行错误;⑤数字全对只多行。
练习 2(找错并写最小用例):一位同学的 P3130 把堆里的报酬存成负数、撤销时弹出「报酬最大」的。写出它的错误类型、一个最小用例(含期望与实际表现)和修复动作。
展开练习 2 答案
类型:建模(撤销的键反了)。最小用例:T = 3、(1,5)(1,3)(2,4)(3,2)——第 2 个任务接下后超期,正确撤销报酬最小的 3 得 11,错误程序撤销 5 得 9。修复:堆里存正的报酬、弹出最小(AI021 才存负数弹最大)。复测:T = 2 → 9、T = 1 → 5 仍通过。
练习 3(三套写法默写):不看资料,各写一遍并用一个断言验证——① 排序 + 一遍扫描 / 双指针(区间调度、救生艇);② 先接下、超期撤销(堆);③ 二分答案(判定函数 + 两种中点方向)。
展开练习 3 答案
每套写法的参考实现都在对应课的展开区:① 第 1 课 练习 4、5(max_shows / min_boats);② 第 2 课 练习 4、5(max_tasks / max_reward);③ 第 3 课 练习 4、5(min_max_piece / max_min_board)。默写后与之对照,差异处就是要再练的点。
练习 4(没有错题的同学):把本模块印象最浅的一道必做题不看旧代码独立重写并用第 06 节的清单复测;然后按兴趣选一个进阶专题起步:字典树(Trie,词库匹配加速)、KMP(单模式串匹配)、并查集进阶、扫描线——这部分不计入本课时长,也不是必做,阶段测验与模拟考试不依赖它们。
08 / 复习入口与完成条件
订正完成之后做什么
订正只针对本模块;每类错误对应的课入口如下,订正完成后按顺序进入模块 6。
| 错误类型 | 回到哪里 | 重点看什么 |
|---|---|---|
| 读题(等号、附加规则、并列规则) | 贪心算法与交换论证、文本规则模拟与 BPE 子词合并 | 第 1 课 第 05 节等号情形、第 5 课 第 05 节四条规则表 |
| 建模(排序键、撤销的键、中点方向、返回值分工) | 堆优化的任务调度、二分答案与贪心判定、树的递归与返回值设计 | 第 2 课 第 04 节交换论证、第 3 课 第 06 节中点表、第 4 课 第 07 节分工表 |
| 边界(只剩一人、空堆、开局状态) | 贪心算法与交换论证 | 第 06 节 l == r 那一轮;上一课第 02~04 节的边界例 |
| 复杂度 / 语言(线性找最大、判定里排序、递归上限、别名) | 堆优化的任务调度、树的递归与返回值设计 | 第 2 课 第 08 节复杂度表、第 4 课 第 07 节递归深度 |
| 输出格式(提前结束不补行、分隔符) | 文本规则模拟与 BPE 子词合并 | 第 05 节「三个需要核对的实现细节」 |
完成前核对页面下方的四项学习检查,尤其不要漏掉两道复习题的独立重写与计时;记录问题不能代替完成这些检查。订正后还有余力,可以再重写本模块最没把握的一道题,或选学字典树、KMP、并查集进阶、扫描线。完成本课后进入模块 6。
09 / 练习
按顺序完成本课的任务
编程任务已通过 0/2 道
错题登记:每题一条,逐条订正
代码自测自主练习练习重点:上一课「模块 5 · 阶段测验」的三题 + 本模块未通过的题,逐题按六类错误原因登记,写清触发用例与错误原因;预计用时:15 分钟
完成标准:错误原因记录表能直接指出本课要回到哪一课复习
需要时查看提示
分不清「建模」还是「边界」时问自己:思路对了只错在特例,是边界;思路本身不成立,是建模。若无法确定类别,先记录具体出错位置,订正后再重新判断类别。登记模板与填好的示例见第 05 节。
P3130 · 最大报酬 · 独立重写
重做任务 1练习重点:堆里存什么、超期时撤销什么、总时间上限 T;预计用时:20 分钟
完成标准:从空文件写到通过全部测试,并记录本次用时供后续比较
需要时查看提示
独立重写时遇到困难超过 10 分钟:先暂停编码,把模块 5 · 第 2 课 的报酬推演表(答案 11)在纸上重新推演一遍再继续。用第 06 节清单里「漏掉 min(k, T)」的用例验证。
P2488 · 中文分词模拟器 · 同类强化
重做任务 2练习重点:标点断句与最长匹配的分支完备性;预计用时:25 分钟
完成标准:每个分支都能指出对应的题目规则
需要时查看提示
这是模块 5 · 第 5 课 做过的题,本课重做它,重点不在通过,而在核对每条题目规则都有对应实现,并检查是否额外加入了题目没有要求的行为。用第 06 节清单里含 ilove 的词库验证最长匹配。
提交结果
提交结果说明与处理方法
- WA
答案错误
独立重写仍未通过:对比两次失败用例和代码位置,重新检查错误原因并更新登记表(第 06 节末段)
- PE
格式错误
还是格式错就把输出段单独抽出来,和样例逐字节对比;提前结束不补空行
- RE
运行错误
语言类错题:写 3 行小实验验证(空堆 heappop、按逗号拆、递归上限)
- TLE
超时
每步线性找最大把 O(n log n) 变成 O(n²)、判定函数里排序让每次判定多一个对数因子、出队才标记会重复入队——记成复杂度类错题
- AC
通过
在登记表中将这条错题标记为已完成;所有错题完成修改、通过验证并更新状态后,本课的订正任务完成
10 / 学习完成检查
本课学习完成检查
完成本课需要:没有新的必做题,勾选全部学习完成检查即算完成。登录后,勾选记录会保存到账号,并更新课程总览的完成状态。