跳转至

递归、递推与 DP

方法速览

任务 方法 口诀
手算递归 代入展开成递归树,叶子的值逐层回代 展开再回收
递归层数过多 栈空间溢出(每层一帧) 递归吃栈
朴素递归慢的原因 重叠子问题被重复计算 重算子问题
分治递归式 展开求和:\(T(n)=2T(n/2)+O(n^2)\)\(O(n^2)\) 层层求和
期望/平均值 全部情形的收益总和 ÷ 情形数 枚举求平均

手算递归

【是什么】 把函数调用按定义展开成调用树,先算叶子再逐层回代。

【怎么算】

  1. 按定义一层层展开到边界;
  2. 从最内层算起,值向外传。

【例】(2022 真题)Ackermann 函数求 ack(2,2)

步骤 展开
1 \(ack(2,2) = ack(1, ack(2,1))\)
2 \(ack(2,1) = ack(1, ack(2,0)) = ack(1, ack(1,1))\)
3 \(ack(1,1) = ack(0, ack(1,0)) = ack(0, ack(0,1)) = ack(0,2) = 3\)
4 \(ack(2,1) = ack(1,3) = 5\)
5 \(ack(2,2) = ack(1,5) = 7\)

规律:\(ack(1,n) = n+2\)\(ack(2,n) = 2n+3\)——验证 \(ack(2,2) = 7\) ✓。

【例】(2021 真题)solve(t, n)\(t=1\) 返回 1,否则返回 \(5\cdot solve(t-1,n) \bmod n\)\(solve(23,23) = 5^{22} \bmod 23\),由费马小定理(23 是质数)\(5^{22} \equiv 1 \pmod{23}\)1

递归的代价

  • 栈溢出:每层调用占一帧栈(参数、局部变量、返回地址),层数过多打爆系统分配的栈空间——不是队列/链表/堆(2021 原题);
  • 重叠子问题:朴素递归斐波那契 \(F(n)\)\(F(n-2)\) 被算两遍、越往下越多——所以朴素版 \(O(2^n)\)、DP 版 \(O(n)\),差距根源是重复计算(2025 原题),与调用栈开销无关。

递推与分治递归式

【例】(2024 真题)\(f(1)=1\)\(f(n) = f(n-1) + f(\lfloor n/2\rfloor)\)

n 演算 f(n)
1 边界 1
2 \(f(1)+f(1)\) 2
3 \(f(2)+f(1)\) 3
4 \(f(3)+f(2)\) 5

递归式展开求和(2025 真题):\(T(n) = 2T(n/2) + O(n^2)\)

子问题数 × 单个代价 该层合计
0 \(1 \times n^2\) \(n^2\)
1 \(2 \times (n/2)^2\) \(n^2/2\)
2 \(4 \times (n/4)^2\) \(n^2/4\)

总和 \(n^2(1 + \tfrac12 + \tfrac14 + \cdots) \le 2n^2 = O(n^2)\)——顶层支配,别急着套主定理的 \(n\log n\)(那要合并代价是 \(O(n)\) 才成立)。

DP 手算:0-1 背包

【例】(2025 真题)容量 20,重量 \((7,5,4,3,6)\)、价值 \((15,12,9,7,13)\)

按 0-1 背包(容量倒序)滚动,最终 \(dp[20] = 44\)——枚举验证:取物品 1、3、4、5(重量 \(7+4+3+6 = 20\) 恰满,价值 \(15+9+7+13 = 44\))最优;取 2、3、4、5(重量 18、价值 41)差一截。

练习题目

练习 1 · 递归的展开

题目

int f(int n) { if (n == 1) return 1; return n * f(n - 1); }f(4) =( )

  • A. \(10\)
  • B. \(24\)
  • C. \(4\)
  • D. \(16\)
答案与解析

答案:B

\(4 \times 3 \times 2 \times 1 = 24\)——阶乘的递归实现。

练习 2 · 双递归回代

题目

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

  • A. \(5\)
  • B. \(6\)
  • C. \(13\)
  • D. \(8\)
答案与解析

答案:D

\(g(1..6) = 1,1,2,3,5,8\)——斐波那契。

练习 3 · 条件递归

题目

int h(int n) { if (n == 0) return 2; return h(n/2) + 1; }h(8) =( )

  • A. \(5\)
  • B. \(4\)
  • C. \(6\)
  • D. \(3\)
答案与解析

答案:C

逐层回代:\(h(0)=2\)\(h(1)=3\)\(h(2)=4\)\(h(4)=5\)\(h(8)=6\)——除到底再逐层加回来。

干扰项思路:A 数错层数(少算一层)。

练习 4 · 栈溢出

题目

递归层数过多引发错误,是因为( )

  • A. 堆空间溢出
  • B. 队列空间溢出
  • C. 系统分配的栈空间溢出
  • D. CPU 过热
答案与解析

答案:C

每层调用占一帧帧;局部大数组 + 无限递归几层就爆。

练习 5 · 递归空间

题目

递归 n 层、每层只开 O(1) 局部变量,空间复杂度( )

  • A. \(O(n)\)(栈深 n 层)
  • B. \(O(1)\)
  • C. \(O(\log n)\)
  • D. \(O(n^2)\)
答案与解析

答案:A

空间看递归深度:n 层栈帧并存;时间看子问题数——两者别混。

练习 6 · 朴素递归复杂度

题目

朴素递归计算 \(F(n)\)(斐波那契)的时间复杂度是( )

  • A. \(O(n)\)
  • B. \(O(n \log n)\)
  • C. \(O(2^n)\)
  • D. \(O(n^2)\)
答案与解析

答案:C

每次分裂两个子调用,递归树节点数指数级(严格说是 \(O(\varphi^n)\),选项按 \(2^n\) 记)。

练习 7 · 快慢差距根源

题目

朴素递归斐波那契 \(O(2^n)\)、DP 迭代 \(O(n)\),巨大差异的根本原因是( )

  • A. 递归调用栈开销过大
  • B. 操作系统限制递归深度
  • C. DP 用了更少内存
  • D. 朴素递归存在大量重叠子问题被重复计算
答案与解析

答案:D

\(F(n-2)\) 被重复算了两次起步、越往下越爆炸;记忆化/DP 每个子问题只算一次。

练习 8 · 记忆化

题目

给递归加上“算过的存起来、再要直接取”的缓存后,斐波那契的复杂度变为( )

  • A. \(O(\log n)\)
  • B. \(O(2^n)\)
  • C. \(O(n^2)\)
  • D. \(O(n)\)
答案与解析

答案:D

\(F(1)\dots F(n)\) 各算一次、每次 \(O(1)\)——记忆化搜索与递推等价。

练习 9 · 递推手算

题目

\(a_1 = 2\)\(a_n = 2a_{n-1} + 1\),则 $a_4 = $( )

  • A. \(23\)
  • B. \(31\)
  • C. \(15\)
  • D. \(7\)
答案与解析

答案:A

逐项递推:\(a_2 = 5\)\(a_3 = 11\)\(a_4 = 23\)

干扰项思路:C 是 \(2\times7+1\) 的漏步误算(把 \(a_3\) 错当 7)。

练习 10 · 走楼梯(等差消耗)

题目

\(k\)\(k+1\) 层消耗 \(10k\) 卡,从 1 层爬起消耗满 1000 卡,至少爬到( )层。

  • A. \(13\)
  • B. \(14\)
  • C. \(16\)
  • D. \(15\)
答案与解析

答案:D

累计 \(10(1+2+\cdots+k) = 5k(k+1) \ge 1000\)\(k(k+1) \ge 200\)\(k = 14\)\(14\times15 = 210\));到 \(1 + 14 = 15\) 层。

练习 11 · 递归式(顶层支配)

题目

\(T(n) = 2T(n/2) + O(n^2)\) 的解是( )

  • A. \(O(n\log n)\)
  • B. \(O(n^2)\)
  • C. \(O(n^2 \log n)\)
  • D. \(O(n^3)\)
答案与解析

答案:B

各层代价 \(n^2 + n^2/2 + n^2/4 + \cdots \le 2n^2\)——几何级数收敛,顶层支配

练习 12 · 递归式(标准主定理形)

题目

\(T(n) = 2T(n/2) + O(n)\) 的解是( )

  • A. \(O(n)\)
  • B. \(O(n \log n)\)
  • C. \(O(n^2)\)
  • D. \(O(\log n)\)
答案与解析

答案:B

每层代价都是 \(n\)、共 \(\log n\) 层——归并排序的形状。

练习 13 · 递归式(单分支)

题目

\(T(n) = T(n/2) + O(1)\) 的解是( )

  • A. \(O(1)\)
  • B. \(O(\log n)\)
  • C. \(O(n)\)
  • D. \(O(\sqrt n)\)
答案与解析

答案:B

每次砍半只花 \(O(1)\)——二分查找的形状。

练习 14 · 快速幂的形状

题目

朴素的 pow(x, n) = x * pow(x, n-1) 的时间复杂度是( )

  • A. \(O(n \log n)\)
  • B. \(O(\log n)\)
  • C. \(O(1)\)
  • D. \(O(n)\)
答案与解析

答案:D

一次递归减一 → 链长 n;快速幂每次减半才是 \(O(\log n)\)——“减一 O(n)、减半 O(log)”。

练习 15 · 汉诺塔

题目

汉诺塔 \(T(n) = 2T(n-1) + 1\)\(T(1) = 1\),则 $T(6) = $( )

  • A. \(63\)
  • B. \(64\)
  • C. \(31\)
  • D. \(127\)
答案与解析

答案:A

通项 \(T(n) = 2^n - 1\)\(2^6 - 1 = 63\)

练习 16 · 01 背包小算

题目

容量 8、物品 \((w,v) = (3,4), (5,6), (4,5)\) 的 0-1 背包最优价值是( )

  • A. \(11\)
  • B. \(9\)
  • C. \(10\)
  • D. \(8\)
答案与解析

答案:C

枚举组合:物品 1+2(\(w = 8\) 恰满,\(v = 4+6 = 10\))最优;1+3 得 9、2+3 超重。

练习 17 · 背包贪心失效

题目

0-1 背包不能按“价值/重量比”贪心求解的原因( )

  • A. 物品不能拆分,高性价比物品可能挤占空间使整体不是最优
  • B. 计算太慢
  • C. 数据会溢出
  • D. 没有原因,可以贪心
答案与解析

答案:A

每件不可分割是 0-1 的本性;可拆分(部分背包)才允许贪心。

练习 18 · LCS 转移

题目

LCS 中 \(x_i = y_j\) 时 $dp[i][j] = $( )

  • A. \(\max(dp[i-1][j], dp[i][j-1])\)
  • B. \(dp[i][j-1] + 1\)
  • C. \(dp[i-1][j-1]\)
  • D. \(dp[i-1][j-1] + 1\)
答案与解析

答案:D

字符相等接在左上角答案之后;A 是失配分支。

练习 19 · LCS 手算

题目

ABCAAAABAABABCBABA 的 LCS 长度是( )

  • A. \(4\)
  • B. \(5\)
  • C. \(6\)
  • D. \(7\)
答案与解析

答案:C

按 LCS 递推填 9×9 的 dp 表,程序验证结果为 6;手算容易在多个 A/B 交错处数多,列表格逐格填。

练习 20 · 递归调用次数

题目

朴素递归斐波那契计算 \(F(6)\)\(F(1)=F(2)=1\))共发生( )次函数调用。

  • A. \(15\)
  • B. \(5\)
  • C. \(31\)
  • D. \(8\)
答案与解析

答案:A

调用数 \(C(n) = C(n-1)+C(n-2)+1\)\(C(1)=C(2)=1\) → 1,1,3,5,9,15;\(C(6) = 15\)

练习 21 · 记忆化的调用数

题目

给上一题的递归加上记忆化后,计算 \(F(n)\) 的函数调用次数是( )

  • A. \(O(n)\)(每个 \(F(i)\) 只算一次)
  • B. \(O(2^n)\)
  • C. \(O(n^2)\)
  • D. \(O(1)\)
答案与解析

答案:A

缓存命中直接返回——从指数降到线性。

练习 22 · 费马小定理速算

题目

$5^{22} \bmod 23 = $( )

  • A. \(22\)
  • B. \(1\)
  • C. \(5\)
  • D. \(0\)
答案与解析

答案:B

23 是质数且 \(\gcd(5,23)=1\):费马小定理 \(5^{22} \equiv 1 \pmod{23}\)

练习 23 · 模意义递归

题目

\(f(1) = 1\)\(f(n) = 3f(n-1) \bmod 7\),则 $f(4) = $( )

  • A. \(2\)
  • B. \(3\)
  • C. \(6\)
  • D. \(5\)
答案与解析

答案:C

逐步取模:\(f(2) = 3\)\(f(3) = 2\)\(f(4) = 6\)

干扰项思路:D 是忘了最后一步取模。

练习 24 · 圆环完全平方(模拟)

题目

4 根柱子,相邻圆环编号和必须是完全平方数,从 1 号开始依次放,最多放( )个。

  • A. \(9\)
  • B. \(11\)
  • C. \(7\)
  • D. \(13\)
答案与解析

答案:B

贪心模拟(每环放第一根能放的柱子):放完 11 号后,12 与各柱顶(放不下)——程序模拟验证 11。

练习 25 · 递归与迭代的等价

题目

任何递归算法( )改成迭代。

  • A. 都可以(用显式栈模拟递归栈)
  • B. 只有树递归可以
  • C. 不可以
  • D. 只有简单递归可以
答案与解析

答案:A

递归的本质是系统帮你压栈;自己开个栈就能完全模拟。

练习 26 · 重叠子问题识别

题目

下列递归中没有重叠子问题的是( )

  • A. 斐波那契 \(F(n) = F(n-1) + F(n-2)\)
  • B. \(g(n) = g(n-1) + n\)
  • C. 组合数 \(C(n,k) = C(n-1,k-1) + C(n-1,k)\)
  • D. 朴素递归 LCS
答案与解析

答案:B

\(g\)单链递归,每个 \(g(i)\) 只被算一次;A/C/D 的子调用会大量交叠重复。

历年真题

2020 年 · 第 11 题

题目

小明想通过走楼梯来锻炼身体,假设从第 \(1\) 层走到第 \(2\) 层消耗 \(10\) 卡热量,接着从第 \(2\) 层走到第 \(3\) 层消耗 \(20\) 卡热量,从第 \(3\) 层走到第 \(4\) 层消耗 \(30\) 卡热量,依此类推,从第 \(k\) 层走到第 \(k+1\) 层消耗 \(10k\) 卡热量(\(k>1\))。如果小明想从 \(1\) 层开始,通过连续向上爬楼梯消耗 \(1000\) 卡热量,至少要爬到第几层楼?( )

  • A. \(14\)
  • B. \(16\)
  • C. \(15\)
  • D. \(13\)
答案与解析

答案:C

累计消耗 \(10(1+2+\cdots+k) = 5k(k+1)\)\(k=13\)\(910 < 1000\)\(k=14\)\(1050 \ge 1000\) → 爬过 14 段,到 \(1+14 = 15\) 层。

2021 年 · 第 3 题

题目

在程序运行过程中,如果递归调用的层数过多,可能会由于( )引发错误。

  • A. 系统分配的栈空间溢出
  • B. 系统分配的队列空间溢出
  • C. 系统分配的链表空间溢出
  • D. 系统分配的堆空间溢出
答案与解析

答案:A

递归帧压在上,层数过多即栈溢出。

2021 年 · 第 11 题

题目

有如下递归代码:

1
2
3
solve(t, n):
    if t=1 return 1
    else return 5*solve(t-1,n) mod n

solve(23,23) 的结果为( )。

  • A. \(1\)
  • B. \(7\)
  • C. \(12\)
  • D. \(22\)
答案与解析

答案:A

展开后是 \(5^{22} \bmod 23\);由费马小定理(质数 23)得 \(5^{22} \equiv 1 \pmod{23}\)

2021 年 · 第 12 题

题目

斐波那契数列的定义为:\(F_1=1\)\(F_2=1\)\(F_n=F_{n-1}+F_{n-2}\)\(n\ge3\))。现在用如下程序来计算斐波那契数列的第 \(n\) 项,其时间复杂度为( )。

1
2
3
F(n):
    if n<=2 return 1
    else return F(n-1) + F(n-2)
  • A. \(O(n)\)
  • B. \(O(n^2)\)
  • C. \(O(2^n)\)
  • D. \(O(n\log n)\)
答案与解析

答案:C

每次调用分裂两个子调用,递归树指数级膨胀。

2022 年 · 第 15 题

题目

ack 函数在输入参数 (2,2) 时的返回值为( )。

1
2
3
4
5
unsigned ack(unsigned m, unsigned n) {
    if (m == 0) return n + 1;
    if (n == 0) return ack(m - 1, 1);
    return ack(m - 1, ack(m, n - 1));
}
  • A. \(5\)
  • B. \(7\)
  • C. \(9\)
  • D. \(13\)
答案与解析

答案:B

逐层展开(完整表见教学节):\(ack(2,2) = ack(1,5) = 7\);规律 \(ack(2,n) = 2n+3\)

2023 年 · 第 4 题

题目

假设有 \(n\) 根柱子,需要按照以下规则依次放置编号为 \(1,2,3,\ldots\) 的圆环:每根柱子的底部固定,顶部可以放入圆环;每次从柱子顶部放入圆环时,需要保证任何两个相邻圆环的编号之和是一个完全平方数。请计算当有 \(4\) 根柱子时,最多可以放置( )个圆环。

  • A. \(7\)
  • B. \(9\)
  • C. \(11\)
  • D. \(5\)
答案与解析

答案:C

模拟:每号放第一根满足“与顶和为完全平方”的柱子;1 空放、2 与 1 和 3 ✓、3 与 1 和 4 ✓、4 与 2 和 6 ✓……到 11 号(与各柱顶都不构成平方数)卡住——程序模拟验证 11。

2023 年 · 第 7 题

题目

最长公共子序列长度常常用来衡量两个序列的相似度。给定两个序列 \(X=\{x_1,x_2,x_3,\ldots,x_m\}\)\(Y=\{y_1,y_2,y_3,\ldots,y_n\}\),最长公共子序列(LCS)问题的目标是找到一个最长的新序列 \(Z\),使得 \(Z\) 既是 \(X\) 的子序列,又是 \(Y\) 的子序列,且长度最大。(序列 \(A\)\(B\) 的子序列,当且仅当保持 \(B\) 的元素顺序删除若干元素后可构成 \(A\)。)

序列 ABCAAAABAABABCBABA 的最长公共子序列长度为( )。

  • A. \(4\)
  • B. \(5\)
  • C. \(6\)
  • D. \(7\)
答案与解析

答案:C

按 LCS 递推填表(程序验证)= 6

2024 年 · 第 3 题

题目

在 C++ 中,以下哪个函数调用会造成栈溢出?( )

  • A. int foo() { return 0; }
  • B. int bar() { int x = 1; return x; }
  • C. void baz() { int a[1000]; baz(); }
  • D. void qux() { return; }
答案与解析

答案:C

无递归出口的自调用 + 每帧 4KB 数组,几层就打爆默认栈空间(与 2021 年第 3 题同考点)。

2024 年 · 第 6 题

题目

已知 \(f(1) = 1\),且对于 \(n \ge 2\)\(f(n) = f(n - 1) + f(\lfloor n/2 \rfloor)\),则 \(f(4)\) 的值为( )。

  • A. \(4\)
  • B. \(5\)
  • C. \(6\)
  • D. \(7\)
答案与解析

答案:B

\(f(2) = f(1)+f(1) = 2\)\(f(3) = f(2)+f(1) = 3\)\(f(4) = f(3)+f(2) = 5\)

2025 年 · 第 9 题

题目

一个 \(0\)-\(1\) 背包问题,背包容量为 \(20\)。现有 \(5\) 个物品,其重量和价值分别为 \(7, 5, 4, 3, 6\)\(15, 12, 9, 7, 13\)。装入背包的物品能获得的最大总价值是多少?

  • A. \(43\)
  • B. \(41\)
  • C. \(45\)
  • D. \(44\)
答案与解析

答案:D

取物品 1、3、4、5:重量 \(7+4+3+6 = 20\) 恰满、价值 \(15+9+7+13 = 44\)(程序跑 0-1 背包 dp[20] = 44 验证)。

2025 年 · 第 14 题

题目

斐波那契数列的定义为 \(F(0)=0\)\(F(1)=1\)\(F(n) = F(n-1) + F(n-2)\)。使用朴素递归方法计算 \(F(n)\) 的时间复杂度是指数级的。而使用动态规划(或迭代)方法的时间复杂度是线性的。造成这种巨大差异的根本原因是?

  • A. 递归函数调用栈开销过大
  • B. 操作系统对递归深度有限制
  • C. 朴素递归中存在大量的重叠子问题未被重复利用
  • D. 动态规划使用了更少的数据存储空间
答案与解析

答案:C

重叠子问题重复计算是指数爆炸的根源;栈开销只是常数级放大。

易错小结

  • 手算递归展开成树、先叶后根回代;ack 类先找小规律(\(ack(2,n) = 2n+3\));
  • 递归层数过多 = 栈溢出(2021 原题),与队列/堆无关;
  • 朴素递归 vs DP 的差距根源 = 重叠子问题(2025 原题),不是栈开销;
  • \(T(n)=2T(n/2)+O(n^2)\) = \(O(n^2)\)(几何收敛顶层支配),\(+O(n)\) 才是 \(O(n\log n)\)——别背串
  • 背包题小规模可枚举验证:2025 真题 4 件恰满 44;
  • 质数模下 \(a^{p-1} \equiv 1\)(费马小定理)速算 \(5^{22} \bmod 23\)