这是全卷最大的一块。 3 篇程序,每篇 3 道判断题(1.5 分)+ 2~3 道选择题(3 分),共 40 分。
📝 本章的例子全部来自 E11 里那 7
套真题,每一条结论都是实际编译运行验证过的。
每篇程序后面挂着两类题:
| 题型 | 分值 | 特点 |
|---|---|---|
| 判断题 | 1.5 分 × 3 | 只有「正确 / 错误」,蒙也有 50% |
| 选择题 | 3 分 × 2~3 | 四选一 |
⚠️ 判断题绝不能空着。 一篇 3 道判断题就是 4.5 分,三篇 13.5 分——全空等于送人 13 分。
| 考法 | 例子 | 破法 |
|---|---|---|
| ① 给一组输入问输出对不对 | 「当输入为 2 2 2 时输出为 1.7321」 |
手动模拟算出来 |
| ② 改一行代码问结果变不变 | 「把 i<=n 改成 i*i<=n
结果不变」 |
想清楚这行到底管什么 |
| ③ 问会不会出错/死循环/越界 | 「输入 时不会下标越界」 | 查数组大小、查除零、查递归出口 |
| ④ 问某个断言是否「总是」成立 | 「输出总是四位小数」「答案一定小于 」 | 找反例,特别是边界 |
| ⑤ 问函数在算什么 | 「f 返回的是最长公共子串的长度」 |
看清是子串还是子序列 |
| ⑥ 问某行会不会被执行 | 「第 15 行不会被执行」 | 找到那行的触发条件 |
别「看懂大意」,要「逐行填表」。
拿到程序后在草稿纸上画一张表,每一列一个变量,每一行一次循环:
轮次 | i | j | a[i] | ans | 说明
-----+-----+-----+--------+-------+--------
初 | - | - | - | 0 |
1 | 1 | 0 | 3 | 1 |
2 | 2 | 0 | 1 | 1 |
3 | 3 | 1 | 5 | 2 | ← j 动了
📝 只填题目问到的那几步就够——比如问「输入 8 时输出什么」,就模拟到能看出规律为止,不必跑完。
⚠️ 在脑子里过一遍是这类题唯一的失分原因。 栈/队列的模拟尤其如此(CSP 2025 第 15 题、CSP 2024 第 13 题)。
拿到一段程序,按这个顺序扫一遍,大部分判断题当场就有答案:
int a[1000]; // 合法下标 0 ~ 999
for (int i = 0; i < n; i++) cin >> a[i]; // n = 1001 就越界⚠️ CSP 2021 第 16 题:数组
a[1000],问「
时不会越界」——错。
⚠️ CSP 2019 第 16 题:把 i = 1 改成
i = 0,第一次就算 n % 0 →
程序直接崩溃。
⚠️ 「递归写错了」不等于「一定死循环」。 CSP 2025 第
16 题把 gcd(b, a%b) 改成
gcd(a, a%b)——实测不会死循环(b
仍单调递减到 0),只是返回值恒等于 a。选项
C「陷入死循环」是最诱人的错误答案。
i <= n vs i < n、mid vs
mid-1、n-1 vs
n——差一是阅读程序题的头号考点。
⚠️ 这是判断题 ④ 类的通用破法。
CSP 2023 第 16 题:题面说「输入的所有数都是不超过
1000 的正整数」,但没说能构成三角形。输入
1 1 5 时海伦公式根号内是负数 → 实测输出
nan,所以「程序总是输出四位小数」是错的。
📝 读题面的限制条件时,逐条问:它把什么排除了?没排除的那些呢?
⚠️ 看到「一定」「总是」,第一反应是去试最小的输入。
len 一定小于
」→
时
len=1,
不成立。错。📝 一个 能把三道题的答案从 C 改成 D。
char 输出的是字符不是数字(CSP 2022 第 16 题)char 默认有符号,(char)0xff = -1(CSP 2021
第 17 题)==(CSP 2022 第 18 题)int 会溢出(但要真去查数据范围,见下)⚠️ 溢出要真算,不能想当然。 CSP 2022 第 18
题问「mid * mid 会不会溢出」——实测穷举
的全部二分过程,最大 mid*mid 只有
,远没到
int 上限。所以「有缺陷、应当强转 64
位」这句话是错的。
📝 但这个警觉本身是对的:看到 mid*mid
先想溢出,然后去查数据范围再下结论。
⚠️ CSP 2024 第 18
题:customFunction(2,3)
的返回值是
,但程序输出的是
pow(8,2) = 64。判断题问「返回值为
64」——错。
📝 把题干里的「返回值」「输出」「第一行」「第二行」圈出来。
| 定义 | |
|---|---|
| 子串 | 连续的一段 |
| 子序列 | 可以不连续,但顺序不变 |
⚠️ CSP 2023 第 17
题整道题都建立在这个区别上:f
求的是最长公共子序列,所以 g
判断的并不是「循环同构」。第 (6) 小题
csppsc 和 spsccp
看着不是循环移位,但因为是子序列,实测照样返回 1。
📝 CSP 2022 第 14 题(abcab
有几个不同子串)也在考这一对词。看到就停一下。
⚠️ 同一个 bug,在有的程序里会暴露,在有的程序里完全看不出来。
比如 i <= n
多读一个元素:如果后面还要读别的数据,输入会整体错位(症状明显);如果只是求和,可能一点问题都没有。
📝 所以「改一行问结果变不变」的题,必须针对题目给的具体程序去想,不能套经验。
⚠️ CSP 2023 第 19 题:那个二分找缺失元素的程序,如果缺的恰好是倒数第二个元素,返回值会和「连续」的哨兵值撞车,程序误报「Sequence is consecutive」。实测确认。
📝 这不影响选项判断,但它说明:能过样例的程序也可能是错的。
⚠️ CSP 2021 第 16 题:题目说输出
3 4 3 17 5,实测是
3 4 3 17 4——只差最后一个。
📝 逐个算完,别算前两个对上了就打勾。
认出模型,半道题就到手了。
| 看到这样的代码 | 它在干什么 |
|---|---|
for (; x; x &= x-1) ret++; |
数二进制里 1 的个数(popcount) |
return x & -x; |
取最低位的那个 1(lowbit) |
gcd(a,b) = b ? gcd(b, a%b) : a |
辗转相除求最大公约数 |
for (i=2; i*i<=n; i++) if (n%i==0) |
试除法判素数 / 分解质因数 |
if (!a[i]) { b[m++] = i; } ... if (i%k==0) break; |
线性筛(欧拉筛) |
dp[i] = min(dp[i-1], dp[i-2]) + cost[i-1] |
爬楼梯型一维 DP |
if (x[i]==y[j]) v[i][j]=v[i-1][j-1]+1; else max(...) |
最长公共子序列 LCS |
dp[i][j] = 1 + min(dp[i][j-1], dp[i-1][j], dp[i-1][j-1]) |
编辑距离 |
candidate/count,相同 ++ 不同
--,归零换人 |
摩尔投票(求多数元素) |
while (!q.empty()) { ... 四方向 ... } |
BFS / 洪水填充 |
| 找区间最小值当根、左右递归 | 笛卡尔树(结构同快排) |
📝 这 12 个模型覆盖了 7 套真题里的绝大多数阅读程序题。 认出模型之后,很多判断题不用模拟就能答(比如「LCS 的返回值 ≤ 较短串长度」显然成立)。
1. 先把程序从头到尾读一遍,判断它在算什么(对照上面的模型表)
2. 做「问输出是什么」的题 —— 老实模拟,这类最费时但最确定
3. 做「改一行会怎样」的题 —— 想清楚那行管什么
4. 做「是否总是成立」的题 —— 找反例,先试 n=0、n=1、全相同、全负数
5. 最后回头看有没有空着的判断题,全部填上
⚠️ 前面的判断题往往是后面选择题的钥匙。
CSP 2022 第 17 题:单选题问「输入
100 100 时输出的第一行」,而那个朴素递归
f(100,100) 根本跑不出来(实测
20 2 就调用了一百多万次)。但判断题 (2)
告诉你「输出的两行总是相同」——用第二行的 DP 值
g(100,100) = 7 就能回答。
📝 做完判断题先别急着翻页,想想它们能不能用来算后面的。
for (; x; x &= x-1) ret++; 在算什么?return 的那个数,输出是 cout
打的;CSP 2024 第 18 题里两者差一个平方x 二进制里 1
的个数(popcount)gcd
实测正常终止