CSP-J初赛选择题训练2021:分块刷题与错题复盘
很多人第一次认真做CSPJ初赛选择题训练2021的时候都会经历一个很别扭的阶段十套卷子刷完正确率纹丝不动错的地方还是那几处——补码的边界、二叉树的叶子数、快排的最坏情况、代码段里循环变量到底跑了几轮。对完答案看一眼解析心里默念哦是这样然后下一套继续错。问题不在刷题量在于刷题的方式把选择题当成判断题来做只关心对错不关心每个选项为什么错、错在哪个前提上。这篇东西想聊的就是怎么把一套选择题拆开用——2021年那份卷子恰好是个不错的样本它的题型分布比较均衡难度也没有刻意堆难题用来做系统性训练非常合适。适合刚接触入门级认证、打算把第一轮稳稳拿下的同学也适合带学生、需要一套可复用的训练流程的教练参考。1. 2021年这套选择题的命题画像先把靶子看清楚1.1 十五道单选题的五大板块与权重第一轮卷面里单项选择通常是十五题、每题两分占了整卷的三成。剩下的分数在阅读程序和完善程序上但那两部分本质上还是在考同一批知识点只是换了个让你读代码的包装。所以选择题这一块如果垮了后面两部分基本也撑不住。把2021年那份卷子的单选题按知识点归类大致能分出五个板块板块典型题量常见考法计算机基础与编码3~4题存储单位换算、字符编码、进制与补码、硬件与系统常识数据结构3~4题二叉树性质与计数、图的边数与遍历、栈与队列、哈希算法与复杂度3题左右排序算法特性、时间/空间复杂度判断、算法思想辨识数学与组合计数3~4题排列组合、容斥、同余、递推、概率期望网络与其他常识1~2题协议分层、地址与端口、数据库基本概念这张表不是让你背题量而是让你在做训练计划时知道权重。很多人的复习顺序是先刷自己会的结果数学与组合这块一直拖到最后而那恰恰是题量不小、提分空间又最大的一块。我见过有同学把进制转换练到滚瓜烂熟组合计数题一错就是两三分性价比完全反了。1.2 为什么看着最眼熟的题反而最容易丢分这里有个反直觉的现象卷面上失分最多的往往不是那些一眼看不懂的题而是那些感觉做过一百遍的题。原因很简单熟悉感会让人跳过审题这一步。举几个2021年那类卷子里反复出现的典型场景。题目问n个结点的二叉树的最小高度你脑子里立刻蹦出 log₂n但如果题干写的是完全二叉树或者每个结点最多两个子结点答案的取整方式就不一样题目问下列排序算法中 unstable 的是你知道快排不稳定可选项里同时放了希尔排序和堆排序三个都不稳定这时候考的是你知不知道哪个最稳定地不稳定。这类题的共同点是知识点你都会丢分丢在条件的读法上。所以我在训练里加了一条硬规矩每道选择题读两遍第一遍读题干第二遍专门读限定词。限定词包括最多/最少一定/可能平均/最坏完全/满有向/无向循环/非循环。把这些词用笔圈出来正确率的提升比多刷五套卷子明显得多。1.3 从卷面反推的知识边界哪些可以不深挖训练选择题最容易犯的第二个错误是过度扩张。看到一个不会的名词就去翻资料一晚上过去卷子只做了三道题学到的东西大概率明年也不考。从2021年那份卷子的实际命题来看边界其实画得挺清楚。计算机基础这块考的是够用级别的常识——存储单位的进位关系、常见字符编码的字节数、CPU与内存的基本分工不会去考指令流水线的细节数据结构这块重点在性质与计数的推导不会让你手写红黑树的旋转算法这块认得出算法思想、说得出复杂度就够了不会给一段代码让你精确数出常数因子。我一般会给一个可操作的标准某道题背后的知识点如果推导过程超过三行就只记结论和适用条件如果三行以内能推出来就必须会推。二叉树叶子数公式三行能推背平衡树的几种旋转超过三行记名字和适用场景就够。按这个标准筛一遍复习范围能砍掉三分之一省下来的时间全部投到必须会推的那部分上。2. 进制、补码与编码丢分不在计算在规则边界2.1 补码运算里那条越界就翻转的隐性规则补码题是选择题里的常客也是会算但会错的重灾区。基本规则大家都记得正数的补码是本身负数的补码是原码除符号位外取反再加一。但真正让题目变难的是边界。八位有符号整数能表示的范围是 -128 到 127这个数字很多人背得出来但背完之后不会用。考题经常这样设计给一个运算问结果是多少。比如 -128 取相反数。按数学直觉应该是 128但八位表示不了 128结果还是 -128。这类溢出不报错、直接绕回来的行为正是补码设计的特点也是命题人最喜欢下刀的地方。我在训练时会要求把补码题的判断流程固定成三步一步都不能跳先定字长。题目说八位十六位用 n 位表示第一件事是把位宽抄在草稿纸角落。没有位宽就没有溢出也就没有答案。写出范围。八位就是 [-128, 127]十六位就是 [-32768, 32767]。范围写出来溢出与否一目了然。算完再回到范围内检验。算出的结果如果落在范围外说明发生了回绕需要按模 2ⁿ 折算回来。这三步看起来笨但能拦住绝大部分算对了数、选错了项的情况。我统计过自己做错的补码题超过一半不是不会算而是算完没做第三步检验把一个数学上正确、但在该位宽下不存在的值选了上去。提示补码题里还有一个高频陷阱是无符号与有符号的混用。题干如果只写n位二进制数而没写有无符号一定要看选项——选项里如果出现了负数说明按有符号理解如果全是非负数且最大值是 2ⁿ-1说明按无符号理解。这个判断必须先做否则后面全错。2.2 编码与存储单位的混淆点编码类题目表面简单实际有一堆容易混的细节。字符编码这块常见的是问某个字符集下n个字符占多少字节。这里的关键是分清字符数和字节数是两回事——一个英文字母在一个字节的编码里占一字节一个汉字在双字节编码里占两字节但如果换成变长编码同样是汉字可能占三字节。题目如果只给了字符数量没给编码方式那这道题本身就有问题正常卷面不会这么出所以看到这类题要先确认题干有没有交代编码。存储单位这块的坑在进位。很多人下意识觉得 1KB 1000B因为日常生活里千就是一千。但存储单位用的是 1024 进制1KB 1024B1MB 1024KB。更进一步如果题目里出现带宽或者传输速率那又是另一套进位通常按 1000。这两种进位混在一道题里是最常见的失分点。我给自己的记法是凡是存的按 1024凡是传的按 1000。存量的描述用容量单位传输的描述用速率单位看到单位里的每秒就往 1000 想。这个方法不严谨但应付选择题足够而且比死记硬背靠谱。2.3 一套可以闭眼执行的手算流程进制转换本身没什么难度难在考场上时间紧、心慌容易在中间某一步算错一位后面全崩。我给这套流程定了个规矩任何进制题都在草稿纸上写出完整的中间过程不许心算。十进制转 r 进制用除 r 取余从下往上读r 进制转十进制用按权展开从高位往低位加。这两条谁都知道但真正执行的时候很多人会在余数倒序这一步翻车——算完从下往上抄的时候漏掉一位或者把最高位的余数写到了最低位。一个更稳的做法是先估位数。要转的数如果是 n目标进制是 r那么结果的位数大约是 log_r n 向上取整。比如把 100 转成二进制2⁶ 642⁷ 128所以结果一定是七位。有了位数预期抄余数的时候就能对得上少一位多一位立刻能发现。再补一个提速技巧二进制和十六进制、八进制之间的转换不用绕道十进制。四位二进制对应一位十六进制三位二进制对应一位八进制从左往右分组不足补零直接换。选择题里如果出现二进制转十六进制用分组法比先转十进制再转十六进制快一倍而且不容易错。3. 树与图所有计数题都能从三个母公式推出来3.1 二叉树度数关系式的通用推法二叉树这块题目花样很多但底层其实就靠着几个关系式在转。最核心的一条是叶子结点数 度为2的结点数 1。这条公式我从来不死记因为推起来只要两行。设度为 0、1、2 的结点数分别是 n₀、n₁、n₂。结点总数 n n₀ n₁ n₂。边数方面除根结点外每个结点都有一条入边所以边数 n - 1同时边数也等于所有结点的出度之和即 0·n₀ 1·n₁ 2·n₂。两个式子联立n₀ n₁ n₂ - 1 n₁ 2n₂化简得 n₀ n₂ 1。两行推完比背公式可靠得多而且这个推导过程本身在阅读程序部分也会用到——有些程序题就是让你算树的结点数认得这个关系式题目就从读代码变成了套公式。完全二叉树还有几条常用结论n 个结点的完全二叉树高度是 ⌊log₂n⌋ 1按层序从 1 开始编号时编号为 i 的结点左孩子是 2i右孩子是 2i1父结点是 ⌊i/2⌋。这三条必须滚瓜烂熟因为它们在堆相关的题目里会反复出现而堆本质就是完全二叉树。3.2 哈夫曼树与WPL别急着画整棵树哈夫曼树的题目通常有两种问法给一组权值问构造出的哈夫曼树的带权路径长度WPL是多少或者问某个字符的编码长度。很多人的第一反应是把整棵树画出来然后一层一层算。这个方法没错但慢而且画到四五个叶子之后容易乱。更快的做法是用合并求和的方式直接算 WPL每次取最小的两个权值合并把合并结果累加到总和里然后把合并值放回集合继续直到只剩一个数。累加出来的总和就是 WPL。举个例子权值是 2、3、5、7、11。第一次取 2 和 3合并成 5总和记 5集合变成 5、5、7、11。第二次取 5 和 5合并成 10总和累加到 15集合变成 7、10、11。第三次取 7 和 10合并成 17总和累加到 32集合变成 11、17。最后一次合并成 28总和 60。所以 WPL 是 60。这个方法的好处是不用画图全程只做加法和取最小值草稿纸上就是几行数字出错概率低很多速度也快。考场上时间紧的时候这个差别很实在。3.3 图论题里那些条件没读完的坑图论的选择题最典型的问题是条件读不全。同一个n个结点的图最多有多少条边有向图是 n(n-1)无向图是 n(n-1)/2完全图和不一定是完全图又是两回事。题目里的每一个限定词都在改变答案。我整理过一份图论题限定词清单做题时逐条对照限定词影响有向 / 无向边数公式差一倍简单图 / 允许重边允许重边时最多边数没有意义要看题目是否另有限制连通 / 强连通影响最少边数n 个结点连通至少 n-1 条边完全图边数取到最大树 / 生成树边数固定为 n-1还有一类题考遍历序列。给一棵二叉树的前序和中序问后序是什么。这类题的做法是固定的前序的第一个是根在中序里找到根的位置左边是左子树、右边是右子树然后递归。手算的时候我建议直接在草稿纸上画树画完再写后序比在脑子里递归可靠。画树看着慢但正确率高而且不容易在中途忘记自己算到哪了。4. 排序、复杂度与算法思想选择题里最会骗人的一块4.1 稳定性表格要背但要背对排序算法的稳定性是必考项标准答案大致是冒泡、插入、归并、基数稳定选择、快速、堆、希尔不稳定。这张表必须背但光背表不够因为题目经常问下面哪个排序算法在某种情况下是稳定的这时候考的是你知不知道稳定性是怎么来的。稳定性的本质是排序过程中相等元素之间的相对次序是否可能被交换。冒泡排序只在相邻元素严格逆序时交换相等不换所以稳定选择排序要把最小值换到前面这个交换可能跨过相等元素所以不稳定快速排序的分区过程会把元素甩到很远的位置也不稳定。理解了这一层某种情况下是否稳定这种题就不用猜了。希尔排序是个特例它的稳定性依赖于增量序列一般按不稳定处理。题目如果专门考希尔排序的稳定性通常是想考它不稳定这个结论不会去纠缠具体的增量选择。4.2 平均与最坏的错位复杂度题最常见的套路是把平均和最坏混在一起。快速排序平均 O(n log n)、最坏 O(n²)归并排序两者都是 O(n log n)堆排序两者也都是 O(n log n)。题目如果说下列排序算法中最坏情况下时间复杂度最优的是答案是归并或堆排如果说平均情况下快排、归并、堆排都是 O(n log n)这时候要靠空间复杂度或者其他性质来区分。这类题我一般会先在草稿纸边上写一个小的对照表把几个常考算法的时间平均/最坏、空间、稳定性列出来做完整套卷子再擦掉。写一遍比看十遍记得牢而且写的过程本身就是一次梳理。空间复杂度这块也有坑。归并排序需要 O(n) 的辅助空间快排平均 O(log n)递归栈堆排 O(1)。题目问原地排序时堆排和快排不算递归栈的话都算归并不算。这个区别在选择题里经常出现。4.3 贪心、分治、动态规划的辨识题算法思想的辨识题通常给一段描述或者一个具体问题问这最适合用什么方法解。这类题不考实现考的是判断标准。我的判断顺序是这样的先看问题有没有最优子结构 重叠子问题两个都有就是动态规划只有最优子结构、没有重叠子问题可能是分治也可能是贪心再看能不能每一步都取当前最优且不影响后续能就是贪心。举个典型对比最短路径里单源无负权用贪心每次取当前最近的未访问点任意两点之间用动态规划逐步放宽中间点而n个数里找最大子段和看着像贪心实际要用动态规划因为局部最优不保证全局最优。这类题错一次不可怕可怕的是错完之后没搞清楚为什么。我要求自己每道辨识题都写一句为什么不是另一种方法写不出来就说明没真懂。5. 程序阅读类选择题把读代码变成跟踪状态5.1 手推表的四个必要列阅读程序那部分虽然不是单选题但里面的判断题和小选择题思路和选择题是一回事。核心方法只有一个手推。不许心算不许感觉应该是这样。手推表我固定用四列循环变量、关键中间变量、条件判断结果、输出。以最简单的循环累加为例轮次isum条件 in输出初值10--111真-223真-336真-四列看起来啰嗦但它的价值在于出错时能立刻定位。如果最终答案不对你可以沿着表格往回找看是哪一轮的哪个变量算错了如果是纯心算错了只能重来。5.2 位运算常见套路库程序阅读题里位运算出现频率很高因为代码短、效果明显。常见的几个套路必须认得n (n-1)把 n 的最低位的 1 变成 0。连续做直到 n 为 0次数就是 n 的二进制中 1 的个数。n (-n)取出 n 的最低位的 1 及其后面的 0得到 lowbit。n ^ (n 1)格雷码转换。x k等价于乘 2ᵏx k等价于除以 2ᵏ 向下取整。认得这些套路很多看起来复杂的代码段一眼就能看出在干什么。比如一段循环里反复执行n n-1并计数那不管变量名起得多花哨它就是在数 1 的个数。5.3 递归题的展开深度控制递归是最容易让人在草稿纸上写乱的部分。我的做法是只展开到能看出规律为止不展开到底。比如一个递归函数参数每次减一边界是 0 或 1那展开三四层就能看出它是线性的直接写通项如果参数每次减半展开三层看是否是二分如果参数从 n 变成 n-1 和 n-2斐波那契式那要警惕指数级复杂度题目很可能是问这段代码的时间复杂度。判断递归复杂度有个粗办法数一数递归树每一层的结点数和层数。每层结点数不变、层数是 log n 的是 O(n log n)每层结点数翻倍、层数是 n 的是指数级。这个方法不精确但选择题够用。6. 三轮刷题节奏从分块专练到限时整卷6.1 第一轮按知识块切不按年份切第一轮的目标是补洞不是提分。所以这一轮不要按年份成套做而是按知识块做。具体做法是把2021年那份卷子的选择题按第一章的五个板块拆开同一板块的题集中做做完立刻复盘。这样做的原因是同类题集中出现时错误模式会暴露得更明显。如果一道进制题错了你只会觉得这题没看清但如果连着十道进制题错了四道而且错法一样那就是方法有问题需要改流程不是改态度。第一轮的题量不一定要大。我一般建议每个板块做两到三套卷子对应的题就够了重点是每道错题都要写清错因。6.2 第二轮限时整卷与错因打标第二轮开始做整卷但有一个硬要求限时而且记录每题耗时。选择题部分的合理节奏是十五题用二十分钟左右平均一题八十秒。有些题会拖时间比如程序阅读类的复杂代码这种允许超到三分钟但要有意识地补偿——后面的题必须加速。记录耗时的方法是做完整卷后在每道题号旁边画正字。做完之后你会看到一批耗时超过三分钟但做对了的题这些是隐患考场上如果状态不好这些题就会变成错题。同时给每道错题打错因标签我一般用这几类知识盲区、审题失误、计算错误、规则混淆、时间不够。标签的作用是让复盘有针对性知识盲区去补知识审题失误去改读题习惯这两类问题的解法完全不同。6.3 第三轮只刷错题和同源题第三轮不再做新卷子只做错题而且要做同源题——也就是同一知识点、不同问法的题。具体做法是错题本上每道题的旁边写一个知识点标签比如补码溢出、完全二叉树高度、快排最坏。然后找同一个标签下的其他题再做一遍。这样做的好处是检验你是否真的掌握了这个知识点而不是记住了某道题的答案。第三轮的时间应该最短一般占整个复习时间的两成左右。如果第三轮发现自己还在大面积错同一种类型的题说明第一轮的方法没执行到位需要回去重来。6.4 时间分配与答题顺序考场上的时间分配我建议按稳拿分优先的原则。选择题里通常会有一两道一眼能看出答案的送分题也有一两道需要想一想的。我的顺序是先扫一遍把一眼就会的做完再回头处理需要想的最后处理完全没思路的。不建议在一道题上死磕。选择题每题才两分花五分钟做对一道不如花五分钟检查五道。如果一道题超过九十秒还没有明确方向先标记、跳过等全部做完再回来。还有一点选择题做完之后不要立刻进入下一部分留两三分钟快速检查一遍答题卡填涂。涂错位这种情况每年都有检查成本极低收益极高。7. 错题本的具体写法与复盘节奏7.1 一条错题记录该有哪几个字段错题本不是抄题本。抄题干、抄解析、抄答案抄完一遍什么也没记住。我要求每条记录只写五个字段题干关键条件不超过两行只写改变答案的那些条件我的错误答案 当时的想法写清为什么选了这个正确答案 一句话理由错因标签同源题索引这个知识点还出现在哪几套卷子里第三项和第五项是核心。写清为什么错比抄十遍正确解答有用而同源题索引能让你在复习时快速找到同类题。7.2 复习周期的安排错题复习用间隔重复的思路做完当天看一遍三天后看一遍一周后看一遍考前再看一遍。关键在第二遍和第三遍。第一遍刚做完题目还热乎看一眼就懂三天后看的时候如果还能立刻说出错因和正确思路说明真掌握了如果卡住了说明只是短期记忆需要重新过一遍知识点。我自己的经验是第二遍卡住的题往往是那种当时看解析觉得很简单的题。这类题最危险因为简单感是解析给的不是你自己想出来的。7.3 一个模拟案例的完整复盘假设有同学做错了一道题题干大意是一棵有 n 个结点的完全二叉树叶子结点的个数是多少他选了 n/2正确答案是向上取整的 (n1)/2。按五个字段记录会是这样关键条件是完全二叉树和叶子结点数。他的想法是完全二叉树叶子大概占一半直接写了 n/2没有验证。正确答案是 ⌈(n1)/2⌉理由是可以用 n₀ n₂ 1 加上完全二叉树的性质推出——完全二叉树里度为 1 的结点最多只有一个。错因标签是公式推导不熟。同源题索引指向另外两套卷子里考完全二叉树高度和结点编号关系的题。这样一条记录写完下次再遇到完全二叉树的题他会先想能不能用 n₀ n₂ 1 推而不是凭感觉估一半。这才是复盘真正起作用的地方。8. 考场上那四十分钟的取舍策略8.1 卡住超过九十秒的处理方式选择题最容易出问题的时刻是遇到一道明明记得但就是想不起来的题。这种状态下人会不自觉地反复读题时间一点点流走。我的处理方式是九十秒原则一道题超过九十秒还没形成明确思路立刻标记跳过。跳过之后不要在脑子里继续想它因为分心会让后面的题也做不好。等全部做完回头再看往往五分钟想不出来的题换个心情两分钟就通了。还有一种情况是排除到只剩两个选项但选不出来。这种时候如果实在选不出就选那个看起来更规范的——出题人设计干扰项时通常会让错误选项带一点过于绝对或过于特殊的味道。这是玄学但在没有更好办法的时候比随机猜略好。8.2 检查阶段到底该查什么检查不是把题重做一遍那等于做两遍题时间不够。检查要有重点第一查填涂。答题卡上的选项和试卷上写的是否一致尤其是中间跳过又回来做的那些题最容易错位。第二查标记过的题。那些九十秒原则跳过的题这时候重新看往往有新思路。第三查限定词。最多/最少一定/可能这些词在匆忙中很容易看漏。我一般会把标记过的题的限定词再读一遍。第四查计算类题的最后一步。进制转换、树的高度、WPL 这类题最后一步是最容易出错的重新核算一次末步的成本很低。9. 几个我一直没改掉的坏习惯以及改法第一个坏习惯是看解析只看对的。做错的题看解析确认正确答案之后就翻页不去看错误选项为什么错。后来发现很多干扰项的设计是有套路的比如把一个常见公式里的加一去掉或者把最坏换成平均。看懂干扰项比看懂正确答案更有价值因为正确答案只有一个干扰项会反复出现。第二个坏习惯是用刷题量代替复盘量。一天做三套卷子感觉很充实但每套只花十分钟复盘错的东西还是错的。后来我把节奏改成一天一套复盘时间比做题时间还长正确率反而上去了。做题是发现问题复盘才是解决问题顺序不能倒。第三个坏习惯是回避不擅长的板块。数学与组合计数一直是我的弱项做题时会下意识先做别的把组合题留到最后最后往往没时间做。改法是强制自己每天先做五道组合题做不完不做其他。坚持两周之后那种看到组合题就烦的情绪会淡很多正确率也跟着上来。最后分享一个我觉得最有用的技巧把每道错题的正确思路压缩成一句话写在便利贴上贴在每天能看到的地方。比如完全二叉树叶子数用 n₀n₂1 推、快排最坏 O(n²) 因为每次只分出一个、补码算完要回到范围内检验。一周换一批一个月下来能积累二三十句这些句子比任何一本教材都贴身因为每一句都是你自己踩过的坑。