🏠

CSP-J 2023 入门组初赛 C++

选择题 · 阅读程序 · 完善程序 完整答案 + 通俗详解

📅 2023年9月16日 📝 选择题 15题 × 2分 📘 阅读程序 40分 🛠️ 完善程序 30分 🎯 总分 100分
正在载入题库…

📝 一、单项选择题(共 15 题,每题 2 分,共 30 分)

单选题,每题只有一个正确答案。覆盖计算机基础、数学、C++ 语法、算法与数据结构。

1. 易 关于计算机病毒,下列说法正确的是( )。
A. 计算机病毒是一种生物病毒 B. 计算机病毒是一种程序 C. 病毒只在 Windows 系统下存在 D. 病毒不会通过网络传播
✅ 正确答案:B
解析:计算机病毒是一段具有破坏性、能自我复制的程序。它不是生物病毒,且任何系统都可能被感染。
2. 易 一个 32 位 int 类型的最大值约为( )。
A. 2×10⁹ B. 2×10⁸ C. 2³¹ D. 2³²
✅ 正确答案:A
解析:32 位有符号 int 范围 -231 ~ 231-1,最大值约 2.147×10⁹ ≈ 2×10⁹。
3. 易 表达式 0xA & 0x5 的值是( )。
A. 0 B. 5 C. 10 D. 15
✅ 正确答案:A
解析:0xA = 1010, 0x5 = 0101;按位与 = 0000 = 0(对应位不同时为 0)。
4. 易 下列 4 个数中,最接近 2⁶ 的是( )。
A. 60 B. 65 C. 70 D. 75
✅ 正确答案:B
解析:26 = 64,最接近的数是 65。
5. 易 一棵二叉树的先序遍历为 ABC,中序为 BAC,则后序为( )。
A. BCA B. ABC C. CAB D. CBA
✅ 正确答案:A
解析:先序 A_,中序 _A_,B、C 在 A 的左/右子树下。
A 是根;中序 BAC → 左 B 右 C;先序 ABC → 左子树先 B、右子树后 C。
所以结构:根 A,左 B(叶子),右 C(叶子)。后序:B C A,即 BCA。
6. 中 C++ 中下列关键字不属于面向对象特性的是( )。
A. class B. public C. virtual D. continue
✅ 正确答案:D
解析:continue 是循环控制关键字;class/public/virtual 都是 OOP 关键字。
7. 中 一个数组 a[5],下标合法范围是( )。
A. 1 ~ 5 B. 0 ~ 4 C. 0 ~ 5 D. 1 ~ 4
✅ 正确答案:B
解析:C/C++ 数组从 0 开始,大小为 5 的数组下标 0~4。
8. 中 下列关于函数重载的说法,正确的是( )。
A. 参数名不同 B. 返回类型不同 C. 参数个数或类型不同 D. 函数体不同
✅ 正确答案:C
解析:重载规则:函数名相同、参数列表不同(个数或类型);不能仅靠返回类型重载。
9. 中 关于 C++ 指针,下列说法错误的是( )。
A. 指针存的是地址 B. 指针可为空 C. 指针类型决定指向对象类型 D. 指针运算 +1 增加 1 字节
✅ 正确答案:D
解析:D 错。指针 +1 是按指向类型大小偏移(如 int* +1 偏移 4 字节),不是 +1 字节。
10. 中 冒泡排序对 n 个数排序,平均时间复杂度是( )。
A. O(n) B. O(n log n) C. O(n²) D. O(log n)
✅ 正确答案:C
解析:冒泡排序平均 O(n²),最好情况(已排序)O(n)。
11. 中 一个有 5 个顶点、3 条边的连通无向图,最少去掉( )条边能使其变成树。
A. 0 B. 1 C. 2 D. 3
✅ 正确答案:B
解析:连通无向图的生成树边数 = n - 1 = 4。已有 3 条 → 需加 1 条使其连通成树。但题目说"去掉边",那原图 5 顶点 3 边:若连通,边数 3 = 5-1-1 = 差 1 即可成树(少 1 条边)。所以最少加 1 条边。题目要求去掉,需重新理解:要让连通图变树,去掉边数 = 原边数 - (n-1)。但题目 5 顶点 3 边无法连通(除非是特殊情况),按题意取最小可能,所以选 B。
12. 中 C++ 中函数模板 template <typename T> 的作用是( )。
A. 定义宏 B. 编写通用函数 C. 提高执行速度 D. 定义类
✅ 正确答案:B
解析:模板用于编写通用(泛型)函数或类,让代码适用于多种类型。
13. 中 下列不能用于字符串匹配的算法是( )。
A. KMP B. 暴力枚举 C. 哈希 D. Dijkstra
✅ 正确答案:D
解析:Dijkstra 是单源最短路径算法,与字符串匹配无关。
14. 中 关于动态规划(DP),下列说法正确的是( )。
A. DP 没有最优子结构 B. DP 用空间换时间 C. DP 等同于贪心 D. DP 一定比递归快
✅ 正确答案:B
解析:DP 把子问题的解存起来(用空间换时间),避免重复计算。
15. 中 关于 STL 容器 map,下列说法正确的是( )。
A. 基于哈希实现 B. 内部有序 C. 查找 O(1) D. 不允许重复 key
✅ 正确答案:B
解析:map 基于红黑树,内部按键有序,查找 O(log n);D 也对(map 不允许重复 key),但 B 更典型。题目答案按典型为 B;D 选项也对。
📌 2023 年选择题答案速查: B D A A C B C C D C B B D B B
(共 15 题,每题 2 分,本模块满分 30 分)

📖 阅读程序(一):海伦公式求三角形面积

💡 程序功能(通俗版)

输入三角形三边 a、b、c,用 海伦公式 计算面积:

面积 = √[ s(s-a)(s-b)(s-c) ],其中 s = (a+b+c)/2

输出固定 4 位小数。

代码 01 阅读程序源码

C++程序代码(每行前为行号,便于定位)
  1. 01#include <iostream>
  2. 02#include <cmath>
  3. 03using namespace std;
  4. 04double f(double a, double b, double c) {
  5. 05 double s = (a + b + c) / 2;
  6. 06 return sqrt(s * (s - a) * (s - b) * (s - c));
  7. 07}
  8. 08int main() {
  9. 09 cout.flags(ios::fixed);
  10. 10 cout.precision(4);
  11. 11 int a, b, c;
  12. 12 cin >> a >> b >> c;
  13. 13 cout << f(a, b, c) << endl;
  14. 14 return 0;
  15. 15}
▶ 输入样例 1
3 4 5
▶ 输出样例 1
6.0000
▶ 输入样例 2
5 12 13
▶ 输出样例 2
30.0000
🪜 分步模拟(输入 3 4 5)
1 执行L09~L10:cout 设固定小数 4 位输出
2 执行L12:a=3, b=4, c=5
3 执行L13:调用 f(3,4,5),进入 f 函数
4 执行L05:s = (3+4+5)/2 = 6
5 执行L06:sqrt(6·3·2·1) = sqrt(36) = 6
6 回到 main,执行L13:cout 输出 6.0000
🔑 海伦公式:把 a、b、c 看作三角形的三条边。必须保证 a + b > c、a + c > b、b + c > a,否则不算合法三角形。代码没有做这个检查——这意味着输入不合法时可能输出负数(取负号后开根号在实数域无意义)。
第 16 题 判断
输入"2 2 2" 时输出"1.7321"。( )
✅ 答案:正确 ✓

等边三角形 a=b=c=2:

  • s = (2+2+2)/2 = 3
  • 面积 = √(3 × 1 × 1 × 1) = √3 ≈ 1.7320508...
  • 取 4 位小数:1.7321 ✓
第 17 题 判断
该程序中,第 7 行的 "(s - b) * (s - c)" 与 "(s - c) * (s - b)" 对结果的影响是相同的。( )
✅ 答案:正确 ✓

乘法满足交换律,两个因子顺序互换,乘积完全一致。所以返回结果不变。

但要小心:如果写成 sqrt 内有多个加法就不同了——加法交换律也成立,但顺序在浮点运算中影响最小舍入位,可能有微小差异。本题是相等的两个因子相乘,结果精确相等。

第 18 题 判断
若输入的 a、b、c 都不是正整数,则程序不会输出正确的结果。( )
❌ 答案:错误 ✗

abc 是 int,但是函数 f 的形参是 double,cin 把 int 传进去会隐式转换为 double。结果精度正常。

即使输入负数(不是三角形),海伦公式会算出复数(开根号负数);但只要是合法的浮点数,仍然能计算。所以"不是正整数就不正确"是错的。

更精确判断:负数或非三角形输入下,结果不可信。但题干说"都不是正整数"——整数小数都行,所以判断错误。

第 19 题 单选
输入"3 4 5" 输出为( )
A. 6.0000 B. 12.0000 C. 24.0000 D. 30.0000
✅ 答案:A. 6.0000

3-4-5 是经典直角三角形!

  • s = (3+4+5)/2 = 6
  • 面积 = √(6 × 3 × 2 × 1) = √36 = 6.0000 ✓
第 20 题 单选
输入"5 12 13" 输出为( )
A. 24.0000 B. 30.0000 C. 60.0000 D. 120.0000
✅ 答案:B. 30.0000

5-12-13 也是经典直角三角形!

  • s = (5+12+13)/2 = 15
  • 面积 = √(15 × 10 × 3 × 2) = √900 = 30.0000 ✓

📖 阅读程序(二):最长公共子序列 + 旋转字符串判断

💡 程序功能(通俗版)

  • f(x, y):求字符串 x、y 的 最长公共子序列 (LCS) 长度。
  • g(x, y):判断 x 旋转(左右循环移位)能否得到 y。思路:x+x 包含 x 的所有旋转,y 是 x+x 的子串当且仅当 y 是 x 的旋转。

代码 02 阅读程序源码

C++程序代码(每行前为行号,便于定位)
  1. 01#include <iostream>
  2. 02#include <vector>
  3. 03#include <algorithm>
  4. 04using namespace std;
  5. 05int f(string x, string y) {
  6. 06 int m = x.size();
  7. 07 int n = y.size();
  8. 08 vector<vector<int>> v(m + 1, vector<int>(n + 1, 0));
  9. 09 for (int i = 1; i <= m; i++) {
  10. 10 for (int j = 1; j <= n; j++) {
  11. 11 if (x[i - 1] == y[j - 1]) {
  12. 12 v[i][j] = v[i - 1][j - 1] + 1;
  13. 13 } else {
  14. 14 v[i][j] = max(v[i - 1][j], v[i][j - 1]);
  15. 15 }
  16. 16 }
  17. 17 }
  18. 18 return v[m][n];
  19. 19}
  20. 20bool g(string x, string y) {
  21. 21 if (x.size() != y.size()) {
  22. 22 return false;
  23. 23 }
  24. 24 return f(x + x, y) == y.size();
  25. 25}
  26. 26int main() {
  27. 27 string x, y;
  28. 28 cin >> x >> y;
  29. 29 cout << g(x, y) << endl;
  30. 30 return 0;
  31. 31}
▶ 输入样例 1
csp-j p-jcs
▶ 输出样例 1
1  (y 是 x 旋转 2 位,g 返回 true)
▶ 输入样例 2
csppsc spsccp
▶ 输出样例 2
0  (y 不是 x 旋转,g 返回 false)
🪜 分步模拟(输入 "csppsc" / "spsccp" 跟踪 g 函数)
1 执行L21~L23:长度都是 5,通过第一道检查
2 执行L24:调用 f(x+x, y),进入 LCS 函数
3 执行L06~L08:m=11, n=5,建 (11+1)×(5+1) 的 DP 表
4 执行L09~L17:双重循环填充 DP 表 v[i][j]
5 返回L18:v[11][5],等于 y 长度 5 时表示 y 是 x+x 子串
6 实际计算:"spsccp" 不在 "csppsccsppsc" 中任何位置 → v[11][5] < 5,g 返回 false,main 输出 0
🔑 LCS 经典 DP:
设 $v[i][j]$ 是 x[0..i-1] 和 y[0..j-1] 的 LCS 长度。
• 若 x[i-1] == y[j-1]:$v[i][j] = v[i-1][j-1] + 1$
• 否则:$v[i][j] = \max(v[i-1][j], v[i][j-1])$
第 21 题 判断
f 函数的时间复杂度不超过 O(min(n, m))。( )
❌ 答案:错误 ✗

f 是双重循环 i=1..m, j=1..n,复杂度是 O(m·n),不是 O(min(n,m))。

题干写"不超过 O(min)" 不对,应该是 O(nm)。

第 22 题 判断
f 函数使用了动态规划算法。( )
✅ 答案:正确 ✓

f 函数用 v 二维数组存子问题答案,这是典型的动态规划(DP)。

第 23 题 判断
任意两个非空字符串 x、y 调用 g 一定返回 true。( )
❌ 答案:错误 ✗

显然不对!比如 x="ab", y="ba" 是旋转,但 x="abc", y="xyz" 长度一样但 g 返回 false。

只有"长度相同"还不够,得是旋转才行。

第 24 题 单选
第 19 行的 "v[m][n]" 与 "v[n][m]" 相同( )
A. 必然相同,因为 LCS 长度与串的顺序无关 B. 必然不同,C++ 区分大小写 C. 视字符串内容而定 D. 视字符串长度而定
✅ 答案:A. 必然相同

LCS(x, y) = LCS(y, x),所以二者必然相同。这是个关键的 LCS 对称性!

第 25 题 单选
输入"csp-j p-jcs" 输出为( )
A. 0 B. 1 C. T D. F
✅ 答案:A. 0

x="csp-j",y="p-jcs",长度都是 5。

x+x="csp-jcsp-j",判断 y 是不是子串:

  • "p-jcs" 在 "csp-jcsp-j" 中:是的!起点 2 切出 p-jcs

所以 g 返回 true,bool 输出为 1。但是注意:题目说 g 返回值为 1(即 T),但选项 A 是 0…

实际重新算:x="csp-j" ("c" "s" "p" "-" "j"),x+x="csp-jcsp-j",y="p-jcs"("p" "-" "j" "c" "s")。

y 是 x 的旋转吗?把 x 旋转:csp-j → sp-jc → p-jcs → -jcsp → jcsp- → csp-j

刚好 "p-jcs" 是 x 向左旋转 2 位得到!
所以 g 返回 true(输出 1,非 0)。

本题按设计思路选答案 B. 1。原选项可能是 1,请以官方答案为准。

第 26 题 单选
输入"csppsc spsccp" 输出为( )
A. T B. F C. 0 D. 1
✅ 答案:C. 0 / D. 1 视具体输出格式

x="csppsc",y="spsccp"。

x+x = "csppsccsppsc",y 是子串吗?扫描:

  • 位置 0: csppsc ✗
  • 位置 1: sppscc ✗
  • 位置 2: ppsccs ✗
  • 位置 3: psccsp ✗
  • 位置 4: sccspc ✗
  • 位置 5: ccspcs ✗
  • 位置 6: csppsc ✗

"spsccp" 不在 x+x 中。所以 g 返回 false,输出 0。

选 C. 0。

📖 阅读程序(三):平方数 vs 因子的平方和

💡 程序功能(通俗版)

  • solve1(n) = n²
  • solve2(n) = n 的所有因子的平方和。

输出 solve2(solve1(n)) 和 solve1(solve2(n))。

代码 03 阅读程序源码

C++程序代码(每行前为行号,便于定位)
  1. 01#include <iostream>
  2. 02#include <cmath>
  3. 03using namespace std;
  4. 04int solve1(int n) {
  5. 05 return n * n;
  6. 06}
  7. 07int solve2(int n) {
  8. 08 int sum = 0;
  9. 09 for (int i = 1; i <= sqrt(n); i++) {
  10. 10 if (n % i == 0) {
  11. 11 if (n / i == i) {
  12. 12 sum += i * i;
  13. 13 } else {
  14. 14 sum += i * i + (n / i) * (n / i);
  15. 15 }
  16. 16 }
  17. 17 }
  18. 18 return sum;
  19. 19}
  20. 20int main() {
  21. 21 int n;
  22. 22 cin >> n;
  23. 23 cout << solve2(solve1(n)) << ' ' << solve1(solve2(n)) << endl;
  24. 24 return 0;
  25. 25}
▶ 输入样例 1
5
▶ 输出样例 1
651 676
▶ 输入样例 2
0
▶ 输出样例 2
0 0  (空结果)
🪜 分步模拟(输入 n=5)
1 执行L22:n=5
2 执行L23:先算 solve2(solve1(5))
3 solve1(5):L05 返回 25
4 solve2(25):L09 i 循环到 sqrt(25)=5
5 i=1:L11 n/i=25≠i,加 1² + 25² = 626
6 i=5:L11 n/i=5==i(完全平方),加 5² = 25;sum=651
7 solve2(solve1(5))=651;solve2(5)=1+25=26;solve1(26)=676
8 输出 651 676 ✓
🔑 因子枚举技巧:枚举到 sqrt(n) 即可。当 i 是因子时,n/i 也是因子。一次加两个平方(平方根情况除外)。
第 27 题 判断
solve2 函数中循环变量 i 的最大值为 n。( )
❌ 答案:错误 ✗

循环条件是 i <= sqrt(n)。所以 i 最大约为 √n,远远小于 n。

如果是 i ≤ n,那复杂度就成 O(n),不是高效的 O(√n)。

第 28 题 判断
第 13-14 行的 i 和 (n/i) 一定是不同的因子对( )
❌ 答案:错误 ✗

不对!当 n 是完全平方数时,例如 n=4, i=2:n/i = 2 = i,二者相同。

所以题目的判断不对,需要特判(n/i == i)。这点代码也注意到了——特判了平方根情况只加一份。

第 29 题 判断
solve2(n) = n² + 1 的必要条件是 n 为质数。( )
❌ 答案:错误 ✗

n=质数 p 时,因子为 {1, p},solve2(p) = 1 + p² = p² + 1。确实满足。

但这不是必要条件。还有其他情况也满足,比如 n=2,3,5,7,11 等。充要条件是"只有 1 和 n 两个因子",p 是质数恰好满足这点。

实际上此命题是对的(n 为质数 ⇒ solve2(n) = n²+1)但是不是充要的(n=1 时 solve2(1)=1+1=2=1²+1 也满足)。

按题意"必要条件"应该是 n 为质数 → solve2(n) = n²+1。由于 n=1 也满足,原命题略有问题。但出题意图可能是"n 为质数"是判断的充分条件,所以判错误。

第 30 题 单选
设 p 是质数,则 solve2(p) 等于( )
A. p² + p + 1 B. n² + n + 1 C. n² + 1 D. p⁴ + 2p² + 1
✅ 答案:C. n² + 1(答案用了 n² + 1 的形式)

质数 p 只有两个因子:1 和 p。平方和 = 1² + p² = p² + 1。

选 C 是正确的(题目中的 n 就是 p)。

第 31 题 单选
该程序可以正确处理:( )
A. n = 0 B. n > 0 C. n = 0 且 n = 1 D. n < 0 或 n = 0
✅ 答案:A. n = 0

n=0:solve1(0)=0,solve2(0) 进入循环 i=1..0 一次都不进,返回 0。最终输出 "0 0"。

n=1:solve1(1)=1,solve2(1) 进入循环 i=1,sqrt(1)=1,i*i=1==n,只加 1²=1,sum=1。solve1(1)=1,输出 "1 1"。程序也能处理。

实际上 n ≥ 0 都正确处理。但按选项最贴近的为 A。

第 32 题 单选
输入"5" 输出为( )
A. 651 625 B. 650 729 C. 651 676 D. 652 625
✅ 答案:C. 651 676

n=5:

  • solve2(solve1(5)) = solve2(25) = 25 的因子平方和
    • 1² + 5² + 25² = 1 + 25 + 625 = 651
  • solve1(solve2(5)) = solve1(26) = 26² = 676

输出 651 676。

🛠️ 完善程序(一):寻找连续整数中缺失的那个

💡 题目背景

输入 n 个整数,构成一个连续序列(缺失了某一个数)。例如输入 [0, 1, 2, 4, 5],缺失的是 3。

用二分查找高效找出缺失的那个。

代码 04 完善程序源码

C++程序代码(带 ①~⑤ 填空标记)
  1. 01#include <iostream>
  2. 02#include <vector>
  3. 03using namespace std;
  4. 04int find_missing(vector<int>& nums) {
  5. 05 int left = 0, right = nums.size() - 1;
  6. 06 while (left < right) {
  7. 07 int mid = left + (right - left) / 2;
  8. 08 if (nums[mid] == mid + ①) {
  9. 09 ②
  10. 10 } else {
  11. 11 ③
  12. 12 }
  13. 13 }
  14. 14 return ④;
  15. 15}
  16. 16int main() {
  17. 17 int n;
  18. 18 cin >> n;
  19. 19 vector<int> nums(n);
  20. 20 for (int i = 0; i < n; i++) cin >> nums[i];
  21. 21 int missing_number = find_missing(nums);
  22. 22 if (missing_number == ⑤) {
  23. 23 cout << "Sequence is consecutive" << endl;
  24. 24 } else {
  25. 25 cout << "Missing number is " << missing_number << endl;
  26. 26 }
  27. 27 return 0;
  28. 28}
▶ 输入样例 1
5
0 1 2 4 5
▶ 输出样例 1
Missing number is 3
▶ 输入样例 2
5
0 1 2 3 4
▶ 输出样例 2
Sequence is consecutive
🪜 分步模拟(输入 [0,1,2,4,5] 二分查找缺失值)
1 执行L18~L20:n=5,nums=[0,1,2,4,5]
2 进入 find_missing,L05:left=0, right=4
3 第 1 次循环:mid=2,nums[2]=2,mid+nums[0]=2+0=2,相等 → left=mid+1=3
4 第 2 次循环:left=3, right=4,mid=3,nums[3]=4,mid+nums[0]=3 → nums[3]≠期望 → right=mid=3
5 第 3 次循环:left=3, right=3,退出循环(left < right 不再成立)
6 执行L14:return right+nums[0] = 3+0 = 3 ✓
第 33 题 填空① 偏移常数
A. 1 B. nums[0] C. right D. left
✅ 答案:B. nums[0]

这里的常数是序列的起始值,应该用 nums[0](数组第一个元素)。

如果序列严格按 [nums[0], nums[0]+1, nums[0]+2, ...] 排列,那么 nums[mid] 应该等于 mid + nums[0]。

第 34 题 填空② 左边正常
A. left = mid + 1 B. right = mid - 1 C. right = mid D. left = mid
✅ 答案:A. left = mid + 1

若 nums[mid] == mid + nums[0],说明 mid 这个位置及之前都正常(不缺失),缺失的部分肯定在右侧,所以 left = mid + 1。

第 35 题 填空③ 右边异常
A. left = mid + 1 B. right = mid - 1 C. right = mid D. left = mid
✅ 答案:C. right = mid

若 nums[mid] > mid + nums[0],说明 mid 或之前缺失了数,所以缩小右边界到 mid。

注意:因为 left < right 的循环条件,right = mid 会终止(当 mid==right 时)。

第 36 题 填空④ 返回值
A. left = nums[0] B. right + nums[0] C. mid + nums[0] D. right + 1
✅ 答案:B. right + nums[0]

循环退出时 left == right == 第一个"异常"位置。期望值是 right + nums[0]。

第 37 题 填空⑤ 完整返回
A. nums[0] + n B. nums[0] + n - 1 C. nums[0] + n + 1 D. nums[n - 1]
✅ 答案:A. nums[0] + n

当序列完整(没有缺失)时,缺失的是"末尾的下一个"。n 个数的完整序列最后应该是 nums[0] + n - 1。

如果 find_missing 返回 nums[0] + n 而不是 nums[0] + n - 1,意味着"末尾后那个数"被假报为"缺失"。

所以判完整条件是 missing_number == nums[0] + n(等于"假缺失"值)就说明原始序列是完整的。

这道题属于较巧妙的设计:利用"哨兵"语义判断完整性。选 A。

🛠️ 完善程序(二):编辑距离(Levenshtein Distance)

💡 题目背景

编辑距离:把字符串 str1 变成 str2 所需的最少操作次数,允许的操作有:

  • 删除一个字符
  • 插入一个字符
  • 替换一个字符

这是经典的 DP 问题(最长回文子序列那一类)。

代码 05 完善程序源码

C++程序代码(带 ①~⑤ 填空标记)
  1. 01#include <iostream>
  2. 02#include <string>
  3. 03#include <vector>
  4. 04using namespace std;
  5. 05int min(int x, int y, int z) {
  6. 06 return min(min(x, y), z);
  7. 07}
  8. 08int edit_dist_dp(string str1, string str2) {
  9. 09 int m = str1.length();
  10. 10 int n = str2.length();
  11. 11 vector<vector<int>> dp(m + 1, vector<int>(n + 1));
  12. 12 for (int i = 0; i <= m; i++) {
  13. 13 for (int j = 0; j <= n; j++) {
  14. 14 if (i == 0)
  15. 15 dp[i][j] = ①;
  16. 16 else if (j == 0)
  17. 17 dp[i][j] = ②;
  18. 18 else if (③)
  19. 19 dp[i][j] = ④;
  20. 20 else
  21. 21 dp[i][j] = 1 + min(dp[i][j - 1], dp[i - 1][j], ⑤);
  22. 22 }
  23. 23 }
  24. 24 return dp[m][n];
  25. 25}
  26. 26int main() {
  27. 27 string str1, str2;
  28. 28 cin >> str1 >> str2;
  29. 29 cout << "Mininum number of operation:" << edit_dist_dp(str1, str2) << endl;
  30. 30 return 0;
  31. 31}
▶ 输入样例 1
kitten sitten
▶ 输出样例 1
Mininum number of operation: 1
▶ 输入样例 2
abc abc
▶ 输出样例 2
Mininum number of operation: 0  (完全相同)
🪜 分步模拟(str1="kitten" → str2="sitten")
1 执行L09~L11:m=6, n=6,建 7×7 DP 表 dp[0..6][0..6]
2 执行L12~L13:i=0 边界 dp[0][j]=j(插入 j 个字符)
3 执行L16~L17:j=0 边界 dp[i][0]=i(删除 i 个字符)
4 逐格填表:i=1,k 与 s 不同 → dp[1][1]=1+min(1,1,2)=2
5 i=1,i 与 i 相同 → dp[1][2]=dp[0][1]=1
6 继续推进直到 dp[6][6],最终 dp[6][6]=1(只需把首字母 k 替换为 s)
7 执行L29:输出 Mininum number of operation: 1
🔑 编辑距离 DP 公式:
  • 边界:dp[0][j] = j(空串加 j 个字符),dp[i][0] = i(删 i 个字符)。
  • 字符相同:dp[i][j] = dp[i-1][j-1](无需操作)。
  • 字符不同:dp[i][j] = 1 + min( 删 / 插 / 替 ),对应 dp[i-1][j], dp[i][j-1], dp[i-1][j-1]。
第 38 题 填空① dp[0][j]
A. j B. i C. m D. n
✅ 答案:A. j

当 i=0(str1 是空串),要把空串变成 str2[0..j-1],需要插入 j 个字符。所以 dp[0][j] = j。

第 39 题 填空② dp[i][0]
A. i B. j C. m D. n
✅ 答案:A. i

当 j=0(str2 是空串),要把 str1[0..i-1] 删成空,需要删 i 个字符。所以 dp[i][0] = i。

第 40 题 填空③ 字符相等判断
A. str1[i - 1] == str2[j - 1] B. str1[i] == str2[j] C. str1[i - 1] != str2[j - 1] D. str1[i] != str2[j]
✅ 答案:A. str1[i - 1] == str2[j - 1]

标准 DP 对比:str1[i-1] 对应 str1 的第 i 个字符(i 从 1 开始),str2[j-1] 对应 str2 的第 j 个字符。比较时索引偏移 1。

第 41 题 填空④ 相等时取值
A. dp[i - 1][j - 1] + 1 B. dp[i - 1][j - 1] C. dp[i - 1][j] D. dp[i][j - 1]
✅ 答案:B. dp[i - 1][j - 1]

字符相同时,不需要操作。前面的子问题答案已经算好,直接继承 dp[i-1][j-1]。

第 42 题 填空⑤ 替换代价
A. dp[i][j] + 1 B. dp[i - 1][j - 1] + 1 C. dp[i - 1][j - 1] D. dp[i][j]
✅ 答案:B. dp[i - 1][j - 1] + 1

字符不同,三选一取最小 + 1。当前已经是 1 + min(...),所以三个选项填的是三种操作之一:

  • 插入 对应 dp[i][j-1](已写在代码中)
  • 删除 对应 dp[i-1][j](已写在代码中)
  • 替换 对应 dp[i-1][j-1] + 1(本题填空)

🎓 学习建议 & 答题策略

★ 阅读程序做题技巧
  1. 先理解程序整体用途(题目开头往往有提示)。
  2. 对每个函数标"输入 → 输出"。
  3. 看 main 里怎么调用。
  4. 判断题要小心"否定/肯定/可能/一定"的措辞。
  5. 选择题要亲手跑一下数据。
★ 完善程序做题技巧
  1. 读题:把题目的"自然语言描述"翻译成"程序要做什么"。
  2. 对着选项逐一排除(看类型、看运算)。
  3. 用代码上下文反推。
★ 本套核心知识点
章节 主要考点
数学海伦公式、直角三角形识别
字符串LCS、字符串旋转判断
因子√n 内枚举、平方根特判
二分查找断点查找、二分模板
动态规划编辑距离 DP