先说结论

阅读程序题不是考你会不会写代码,而是考你能不能像 CPU 一样,一行一行把程序执行出来。

而人脑的工作记忆只有三四个格子——变量一多、递归一深,靠「心算」必错。

草稿纸才是你的第二块内存。


这 40 分到底考什么

题型 题量 分值 建议用时 难度
单项选择题 15 题 30 分 20—25 分钟
阅读程序题 3 大题 40 分 40—50 分钟 ★★★
完善程序题 2 大题 30 分 30—40 分钟 ★★

阅读程序题里常见的问法就这几类:

  • 给输入求输出:代码给定输入,问最后输出什么;
  • 问程序功能:这段代码实现了什么算法;
  • 判断说法正误:判断题正确选 A、错误选 B,别搞反
  • 问边界条件n 取 0、取 1 时会怎样;
  • 问时间复杂度:这段代码跑一遍要多久。

手动模拟三步法

别一上来就盯着代码一行一行硬算,按这三步走:

阅读程序题·手动模拟三步法

第 1 步:30 秒定框架

先快速扫一遍代码,看函数名、注释、循环结构、有没有递归、主函数在干什么:

  • 看到 f(n) = n * f(n-1) → 阶乘
  • 看到双层循环 + 相邻比较交换 → 冒泡排序
  • 看到 f(n) = f(n-1) + f(n-2) → 斐波那契

方向对了,读起来就不慌。

第 2 步:模拟执行,画变量表(核心,2—5 分钟)

一行一行走,每执行完一行就把变量的新值记进表里,别等循环跑完再回头算。

标准格式:

行号 | 变量1 | 变量2 | … | 输出
  • 循环体每迭代一次,就新开一行;
  • 遇到函数调用,单独画一个调用栈

第 3 步:看问题,回表找答案(30 秒)

判断题直接查你的变量表;选择题用排除法 + 代入法


实战一:递归题 —— 画调用栈

int f(int n) {
    if (n <= 1) return n;          // ① 终止条件
    return f(n - 1) + f(n - 3);    // ② 递归调用
}
int main() {
    cout << f(6);
    return 0;
}

Step 1|定框架f 里两组递归调用,典型「递归求值」题,不用管实际意义,只要算出 f(6)

Step 2|画调用栈,从最底层往上回代。递归不能从 f(6) 往下硬算,要先把最底层能直接算的算出来,再一层层往上代:

f(6)
├─ f(5)
│   ├─ f(4)
│   │   ├─ f(3)
│   │   │   ├─ f(2)
│   │   │   │   ├─ f(1) → 1
│   │   │   │   └─ f(-1) → -1      ← n ≤ 1 直接返回 n
│   │   │   │   所以 f(2) = 1 + (-1) = 0
│   │   │   └─ f(0) → 0
│   │   │   所以 f(3) = 0 + 0 = 0
│   │   └─ f(1) → 1
│   │   所以 f(4) = 0 + 1 = 1
│   └─ f(2) → 0
│   所以 f(5) = 1 + 0 = 1
└─ f(3) → 0
所以 f(6) = 1 + 0 = 1

Step 3|得答案:输出 1。

⚠️ 这里的坑非常典型:终止条件是 return n,不是 return 1 所以当 n < 1 时返回的是那个负数本身(f(-1) = -1),很多人想当然以为「小于 1 就返回 0」,一步错、步步错。


实战二:循环题 —— 画变量表

int s = 0;
for (int i = 1; i <= 5; i++) {
    if (i % 2 == 1) s += i;
    else            s -= i;
}
cout << s;

逐行记录,答案一眼可见:

迭代 i i % 2 操作 s
初始 0
1 1 1 s += 1 1
2 2 0 s -= 2 -1
3 3 1 s += 3 2
4 4 0 s -= 4 -2
5 5 1 s += 5 3

\(s = 1-2+3-4+5 = 3\)

循环与递归的手动模拟法


3 个「必动笔」场景

  1. 循环 → 画变量表。 只要循环里牵扯 2 个以上变量(s 累加、i 变化),就一行一行记下来。
  2. 递归 → 画调用栈。 从最底层往上回代,绝不跳步——递归题九成错在「跳步」上。
  3. 数组 / 指针 → 画下标变化。 特别注意:swap 用引用传参时,形参改了实参就变;但如果两个值本来就相等,「交换」了也看不出变化——别以为代码没执行

3 条铁律

  1. 先看框架,再逐行读。 别一上来就抠细节,先花 30 秒弄清它在干嘛。
  2. 画变量表!画变量表! 3 个以上变量脑子就记不住了,别硬撑。
  3. 递归必画调用栈,从底部往上算。 循环画表,递归画树,两码事。

考点速记

  1. 阅读程序题 3 大题 40 分,建议 40—50 分钟,是初赛分值最高、最拉差距的板块。
  2. 判断题正确选 A、错误选 B,每年都有人搞反。
  3. 递归先看终止条件返回的是什么return n 还是 return 1),再看递归式。
  4. 遇到 n = 0 / 1 / -1 这类边界,一定要单独代入验证。
  5. 草稿纸 + 变量表 > 大脑心算,这是唯一「稳」的办法。

思考题

int g(int n) {
    if (n <= 1) return 1;
    return g(n - 1) + g(n - 2);
}

g(5) 输出多少?如果终止条件改成 return n,结果又是多少?

答案 `return 1` 时:**8**。 ``` g(0)=1 g(1)=1 g(2)=g(1)+g(0)=2 g(3)=g(2)+g(1)=3 g(4)=g(3)+g(2)=5 g(5)=g(4)+g(3)=8 ``` 改成 `return n` 时:**5**。 ``` g(0)=0 g(1)=1 g(2)=1+0=1 g(3)=1+1=2 g(4)=2+1=3 g(5)=3+2=5 ``` **一个字的终止条件差别,结果从 8 变成 5**——这就是为什么要一字一句读终止条件。

参考整理自公众号「开心算法」《CSP 初赛阅读程序题,靠「手动模拟」拿满 40 分》。