资讯动态

CSP-J2024初赛真题解析:从阅读程序到完善程序的避坑指南

发布时间:2026/10/23 16:21:20 来源:尧图企业网站定制
简介这份资料面向备战CSP-J 2024的选手与信息学入门学习者聚焦初赛笔试部分的真题还原与逐题解析帮助读者在刷题中查漏补缺、巩固计算机基础与C语法。内容覆盖单项选择题、阅读程序与完善程序三大板块涉及int存储范围、格雷码、存储单位换算、C基本数据类型与循环语句、ASCII码运算、二分查找效率、无向图度数、二叉树遍历、栈的出栈顺序、排列组合及编译器作用等高频考点并对质数判断、函数封装、数组与字符串输入输出、位运算等代码题给出思路拆解。资源为1个PDF文件压缩包约797KB结构紧凑、便于打印或移动端随时翻阅。目前已有7552人学习下载适合需要系统梳理初赛知识点、对照答案复盘错题、提升编程逻辑与应试能力的初中级选手使用。1. 从一份 CSP-J2024 真题解析说起为什么有人刷完还是不会每年 CSP-J 初赛结束最热闹的不是考场外而是各种群里对答案。我见过太多选手选择题对完答案感觉良好一到阅读程序和完善程序就崩了——不是不会写代码而是读不懂别人写的代码在干什么。这份 CSP-J2024 题目加答案解析恰好把初赛三大题型单选、阅读程序、完善程序的完整解题链路摊开了从 int 存储范围、格雷码、二分查找比较次数这些基础概念到质数统计、动态规划、递归汉诺塔的代码推演每一题都给了题解思路。它适合两类人一是准备 CSP-J/S 初赛的选手想拿它当模考卷加错题本二是带竞赛班的教练想找一份能直接拆解讲法的素材。但我要先说一个反直觉的结论光看解析不做代码复现阅读程序题该错还是错因为初赛考的不是“你知不知道答案”而是“你能不能在大脑里跑一遍程序”。2. 单项选择题的底层知识图谱从 int 范围到排列组合2.1 数据类型与存储单位那些年年考年年错的点单项选择题前 15 题里至少有 5 题是在反复考同一类东西——计算机基础概念。第 1 题问 int 存储范围答案是 -2^31 到 2^31-1也就是约 -21 亿到 21 亿。这个知识点看起来简单但每年都有人栽在“有没有符号位”上。我的建议是不要背十进制范围直接记二进制形式32 位里 1 位符号位加 31 位数值位范围就是 -2^31 到 2^31-1。第 5 题考存储单位换算1MB 1024KB 1024×1024 字节 2^20 字节再乘 8 就是 2^23 个二进制位。这类题的核心就一句话字节和位的换算是乘 8KB 和 MB 的换算是乘 1024。第 6 题和第 7 题考 C 语言特性。struct 不是基本数据类型它是复合类型repeat-until 不是 C 的循环语句C 只有 for、while、do-while 三种。这两题属于送分题但如果你平时只写 Python 或者只刷算法题不碰语言细节就容易翻车。第 8 题考 ASCII 码a 的 ASCII 值是 97加 13 得 110对应字符 n。这种题没有技巧就是记住 a97、A65、048 这三个锚点其他字符靠偏移推。2.2 算法与数据结构格雷码、二分查找、二叉树遍历第 4 题考格雷码序列。格雷码的定义是相邻两个数只有一位二进制不同。题目给了四个选项让你判断哪个是 0 到 7 的格雷码序列。解析里说“根据格雷码定义推算即可”但实际操作中你可以用公式 G(n) n ^ (n 1) 快速验证。比如 n0 时 G(0)0n1 时 G(1)1n2 时 G(2)3n3 时 G(3)2以此类推得到 0,1,3,2,6,7,5,4。这个公式在初赛里不会直接考但用它来验证选项比逐个推快得多。第 9 题考二分查找的最多比较次数。有序表中有 n 个元素二分查找最多比较 ⌈log2(n1)⌉ 次。题目里 n 的具体值虽然被吞了但解析给了公式你按公式算就行。第 12 题考二叉树遍历还原已知前序 [A,B,D,E,C,F,G] 和中序 [D,B,E,A,F,C,G]求后序。解析说“还原二叉树模拟发现是深度为 3 的满二叉树”后序结果是 [D,E,B,F,G,C,A]。这类题的手工做法是前序第一个是根拿根去中序里切左右子树然后递归。我一般建议在草稿纸上画树别硬推。第 11 题考无向图度数之和等于边数两倍第 13 题考栈的出栈顺序可能性第 14 题考捆绑法排列组合。这三题分别对应图论、栈、组合数学的基础结论。第 13 题的 D 选项之所以不可能是因为它违反了栈的后进先出约束——具体来说如果某个元素先出栈比它晚入栈的元素不可能比它更早出栈。这个判断方法比逐个模拟快。提示单项选择题的 15 题里纯记忆型题目数据类型、操作系统、编译器作用大约占 6 题剩下 9 题都需要你动手推。如果你发现自己在这 9 题上花的时间超过 15 分钟说明基础算法的熟练度还不够。3. 阅读程序题的三段式拆解质数统计、动态规划与递归3.1 程序一质数判断与统计的代码推演程序一给了三个函数isPrime 判断质数countPrimes 统计 1 到 n 的质数个数sumPrimes 求 1 到 n 的质数和。主函数读入 x输出 countPrimes(x) 和 sumPrimes(x)。第 16 题问输入 10 时输出什么答案是 4 和 17因为 10 以内质数是 2、3、5、7。第 17 题问把 isPrime 里的 i*in 改成 in/2 后输入 20 时 countPrimes 输出是否变为 6。答案是错误因为改条件只影响效率不影响结果20 以内仍有 8 个质数。第 19 题问输入 50 时 sumPrimes 输出多少答案是 328。这个需要你实际算一下 50 以内所有质数的和23571113171923293137414347 328。第 20 题问把 for(int i2;i*in;i) 改成 for(int i2;in;i) 后输出是否变化答案是仍然输出 4 和 17因为循环条件放宽不会改变判断结果只是效率降低。这里有一个容易被忽略的点isPrime 函数里 iin 这个条件当 n 很大时 ii 可能溢出 int 范围。虽然初赛不考溢出但你在实际写代码时应该用 in/i 或者 long long。这是从初赛题延伸到实际编程的一个典型例子。3.2 程序二动态规划的“相邻必选一”模型程序二的代码是一个动态规划解析说“给定一个数组要求相邻两个数中必须选一个数最小化选的数的和”。这个描述其实对应的是 LeetCode 上的一道经典题——打家劫舍的变种。代码逻辑是dp[i] min(dp[i-1], dp[i-2]) cost[i-1]最后返回 min(dp[n], dp[n-1])。第 21 题问输入 cost{10,15,20} 时输出什么答案是 15。手工推dp[1]10dp[2]min(10,0)1515dp[3]min(15,10)2030最后 min(30,15)15。第 24 题问输入 {1,100,1,1,1,100,1,1,100,1} 时输出多少答案是 6。这个需要你按 dp 公式逐步算或者理解成“每隔一个选一个”的策略。第 26 题问把 min(dp[i-1],dp[i-2])cost[i-1] 改成 dp[i-1]cost[i-2] 后输入 {5,10,15} 输出什么答案是 10。这个改动实际上破坏了原来的状态转移逻辑你需要重新按新公式模拟。这道题的血泪经验是阅读程序题里的动态规划不要试图理解它在解决什么实际问题直接按代码逐行模拟。因为出题人往往会故意把问题描述得模糊你越想“这到底在干嘛”越容易绕进去。我的做法是拿一张纸把 dp 数组的每个值列出来按循环顺序填表。3.3 程序三递归函数的展开与陷阱程序三的 customFunction(a, b) 逻辑是如果 b0 返回 a否则返回 a customFunction(a, b-1)。这本质上就是计算 a*(b1)。主函数输出 pow(result, 2)。第 27 题问输入 2 3 时 customFunction(2,3) 返回值是否为 64答案是错误因为 customFunction(2,3)22228平方后才是 64。第 28 题问 b 为负数时是否会无限递归答案是正确因为 b 永远不会等于 0。第 31 题问输入 3 3 时最终输出什么答案是 144因为 customFunction(3,3)33331212 的平方是 144。第 32 题是一个 4 分题把函数改成 return a customFunction(a-1, b-1)输入 3 3问最终输出。这个改动后customFunction(3,3) 3 customFunction(2,2) 3 2 customFunction(1,1) 321customFunction(0,0) 3210 6平方后是 36。这道题的关键是注意递归终止条件 b0 时返回的是 a而不是 0所以 customFunction(0,0) 返回 0。注意递归题在初赛里几乎年年出现判断递归层数和返回值时一定要把终止条件单独拿出来看。很多选手错在“以为 b0 时返回 0”实际上返回的是 a。4. 完善程序题的填空逻辑平方数判断与汉诺塔递归4.1 平方数判断枚举平方根的边界与条件完善程序第一题是判断一个正整数是否为完全平方数。代码框架是枚举 i 从某个下限到某个上限如果 num ii 则返回 true否则循环结束后返回 false。第 33 题问 i 的初始值填什么答案是 1因为平方根从 1 开始枚举。第 34 题问 bound 填什么答案是 (int)floor(sqrt(num))因为平方根不会超过 num 的算术平方根。第 35 题问 if 条件填什么答案是 num ii。第 36 题问返回什么答案是 true。第 37 题问循环结束后返回什么答案是 false。这道题的坑在于第 34 题的 bound。如果你填 floor(sqrt(num/2))当 num1 时 bound0循环一次都不执行直接返回 false但 1 是完全平方数。所以 bound 必须覆盖到 sqrt(num)。另外用 ii 而不是 iinum 作为判断条件时要注意 i*i 可能溢出但在初赛范围内 num 不会太大。4.2 汉诺塔递归三步拆解与参数顺序完善程序第二题是汉诺塔。dfs(i, src, tmp, tgt) 表示把 i 个圆盘从 src 借助 tmp 移到 tgt。第 38 题问终止条件 i 等于什么答案是 1因为只剩一个圆盘时直接移动。第 39 题问 move 的参数是什么答案是 src, tgt。第 40 题问第一个递归调用的参数是什么答案是 src, tgt, tmp也就是把 i-1 个圆盘从 src 借助 tgt 移到 tmp。第 41 题问第二个递归调用的参数是什么答案是 tmp, src, tgt也就是把 i-1 个圆盘从 tmp 借助 src 移到 tgt。第 42 题问第二个递归的第一个参数是什么答案是 i-1。汉诺塔的递归逻辑可以用一句话概括先把上面 n-1 个盘子从 A 移到 B再把最大的盘子从 A 移到 C最后把 n-1 个盘子从 B 移到 C。对应到代码里就是 dfs(i-1, src, tgt, tmp)、move(src, tgt)、dfs(i-1, tmp, src, tgt)。参数顺序容易搞混我的记忆方法是第一个递归里tmp 和 tgt 互换因为你要把盘子暂时放到 tmp 上第二个递归里src 和 tmp 互换因为盘子现在在 tmp 上要借助 src 移到 tgt。// 汉诺塔递归的标准写法 void dfs(int i, char src, char tmp, char tgt) { if (i 1) { move(src, tgt); // 只剩一个盘子直接移动 return; } dfs(i - 1, src, tgt, tmp); // 把上面 i-1 个从 src 移到 tmp借助 tgt move(src, tgt); // 把最大的从 src 移到 tgt dfs(i - 1, tmp, src, tgt); // 把 i-1 个从 tmp 移到 tgt借助 src }上面这段代码就是完善程序题的标准答案。逻辑说明第一个递归调用里目标柱从 tgt 变成了 tmp因为你要把上面的盘子暂时挪开第二个递归调用里源柱从 src 变成了 tmp因为盘子现在在 tmp 上。参数说明i 是当前要移动的盘子数src 是源柱tmp 是辅助柱tgt 是目标柱。每次递归 i 减 1直到 i1 时直接移动。5. 避坑与排查初赛刷题时最容易翻车的五个地方5.1 现象阅读程序题看懂了代码但选错答案原因你理解的是代码的“意图”而不是代码的“实际执行结果”。出题人经常在代码里埋一些反直觉的细节比如循环边界、递归终止条件、数组下标从 0 还是 1 开始。解决拿一张草稿纸把关键变量的值逐行写出来。不要跳步不要心算。特别是循环和递归手动模拟前 3 到 5 轮找到规律后再推最终结果。5.2 现象完善程序题填了语法正确但逻辑错误的选项原因你只看了空格所在的那一行没有把整个函数串起来看。比如平方数判断里bound 填 floor(sqrt(num/2)) 语法没问题但当 num1 时逻辑就错了。解决填完所有空之后用一组边界数据最小值、最大值、特殊值代入完整代码跑一遍。平方数判断用 num1 和 num0 测试汉诺塔用 n1 和 n2 测试。5.3 现象选择题在格雷码、二分查找比较次数上反复出错原因这些知识点平时写代码用不到只靠考前突击记忆没有形成推导能力。解决格雷码用公式 G(n)n^(n1) 自己推一遍 0 到 7 的序列二分查找比较次数用 n1、n2、n3、n4 手动模拟找到 ⌈log2(n1)⌉ 的规律。推导一遍比背十遍管用。5.4 现象动态规划题手工模拟时算到一半就乱了原因dp 数组的状态转移涉及前两项手工算容易漏掉初始条件或者搞错下标。解决先把 dp[0] 和 dp[1] 的初始值写出来然后按 i 从 2 到 n 逐个填表。每填一个值在旁边标注它是由哪两个值推出来的。这样即使算错也能回溯。5.5 现象递归题判断递归次数时多算或少算一层原因递归终止条件看错或者把 b0 时返回 a 记成了返回 0。解决把递归函数的终止条件单独抄出来用最简输入比如 b0、b1手动跑一遍。确认终止条件的返回值是什么再往上推。提示初赛刷题的正确姿势是“先模考、再对答案、最后逐题写解析”。对完答案直接看解析效果至少打五折。因为你看解析时会产生“我懂了”的错觉但下次遇到同类题还是不会。6. 从初赛题到实战用 Python 复现质数统计与汉诺塔初赛题里的代码都是 C但如果你平时用 Python 刷题完全可以把这些题改写成 Python 来验证自己的理解。我一般会做一件事把阅读程序题的代码用 Python 重写一遍然后跑几组测试数据看看输出是否和手工推的一致。这一步能帮你发现很多“以为自己懂了”的漏洞。以程序一的质数统计为例Python 版本如下import math def is_prime(n): if n 1: return False for i in range(2, int(math.isqrt(n)) 1): if n % i 0: return False return True def count_primes(n): return sum(1 for i in range(2, n 1) if is_prime(i)) def sum_primes(n): return sum(i for i in range(2, n 1) if is_prime(i)) # 测试输入 10 和 50 print(count_primes(10), sum_primes(10)) # 输出 4 17 print(count_primes(50), sum_primes(50)) # 输出 15 328这段代码的逻辑和 C 版本完全一致但用了 math.isqrt 来避免 i*i 的溢出问题。参数说明is_prime 接收一个整数 n返回布尔值count_primes 和 sum_primes 分别返回质数个数和质数和。你可以把输入改成 20、50、100对比手工推的结果。汉诺塔的 Python 版本更简单def hanoi(n, src, tmp, tgt, moves): if n 1: moves.append((src, tgt)) return hanoi(n - 1, src, tgt, tmp, moves) moves.append((src, tgt)) hanoi(n - 1, tmp, src, tgt, moves) moves [] hanoi(3, A, B, C, moves) for m in moves: print(f从柱子 {m[0]} 挪到柱子 {m[1]}) print(f总共 {len(moves)} 步)跑一遍 n3输出 7 步和初赛题里的递归逻辑完全对应。n4 时输出 15 步n 个盘子的最少步数是 2^n - 1。这个验证过程能帮你确认自己对递归参数顺序的理解是否正确。从那以后我每次带人刷初赛题都强制要求把阅读程序题的代码用另一种语言重写一遍并跑通。因为“看懂”和“写出来”之间隔着一条河而初赛阅读程序题考的就是你能不能过这条河。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑