简介:2024年信息学奥赛CSP-J初赛真题详解面向初中生、少儿编程学员和C++爱好者,帮助备考者系统把握入门组初赛命题脉络。资源包仅含1个docx文档,大小约121KB,但内容精炼,覆盖2024年CSP-J初赛单项选择题、阅读程序题等真实题目,每题均附答案和解析,适合考前冲刺或日常查漏。文档重点剖析了32位int存储范围、格雷码、存储单位换算、进制转换、二分查找、栈与队列、无向图度数、二叉树遍历、排列组合、C++基本数据类型与循环结构、操作系统识别等高频考点,并在解析中穿插组合数学和图论方法,帮助读者拓宽解题思路。借助这份分析,考生可快速理解考点背后的计算机科学原理,提升逻辑推理和代码阅读能力,并通过错题对照明确复习重点。已有2593人学习使用,是信息学奥赛入门阶段高性价比的参考资料。
1. 2024年信息学奥赛CSP-J初赛真题考点拆解:真正拉分的是70分代码题
2024年CSP-J初赛单选题第1题直接问32位int的存储范围,这几乎是明送分,可每年都有考生在C和D选项之间反复纠结,最后凭感觉选错。第2题把八进制、十六进制、二进制混在一个式子里,看着像复杂计算,实际先统一转成十进制再代入就结束。初赛总分100分,单选30分、阅读程序40分、完善程序30分,也就是说70分都在读代码和补代码,光背知识点根本拿不稳,必须按题型练手感。这篇围绕2024年CSP-J初赛真题展开,把单选高频考点、阅读程序常见的三类模型、完善程序的补全套路逐题拆开,最后单列易错点和答题顺序。适合正在备赛的初中生、带赛的教练,也适合正在学少儿编程、准备往信奥方向转的学生,尤其是已经学完C++语法但刷题量不够、一读程序就发懵的选手。
2. 单选高频考点:int范围、进制换算、组合计数与数据结构的送分套路
2.1 整型范围与进制计算:先翻译,再代入
第1题答案选C。int占4字节共32位,用补码表示时范围是-2^31到2^31-1,对应十进制就是-2147483648到2147483647。选项A和B都把下界写成了-2147483647,选项D又把上界多写了一位。这个考点的记忆方法很简单:补码的负数比正数多一个,下界的绝对值比上界大1。32位二进制中最高位是符号位,剩余31位表示数值,所以最大值是2^31-1,最小值是-2^31,不对称。
第2题考的是混合进制计算,答案A。表达式(148−10102)∗D16−11012里,148是八进制,D16中的D是十六进制里的13,11012是二进制。先把它们分别转成十进制:148按权展开是1×8+4=12;D16就是13;11012是1×8+1×4+0×2+1=13。代入原式得到(12-10)×13-13=2×13-13=13。这类题的通用做法是:看到带下标或字母的数字,先统一转十进制再计算,不要拿八进制数字直接当十进制算,也不要看到D就当字母跳过。平时练习时把0到15的二进制、八进制、十六进制对照表默写一遍,混算题基本不会丢分。
2.2 组合与排列:分类乘完再相加,相邻捆绑要记牢
第3题是典型的分类组合计数,答案B。题干是10名员工分3个部门,选4人且每个部门至少1人。因为只选4人,必然有1个部门出2人,另外两个部门各出1人。分三种情况:A部门出2人,B、C各出1人,方案数是C(4,2)×C(3,1)×C(3,1)=6×3×3=54;B部门出2人,A、C各出1人,方案数是C(4,1)×C(3,2)×C(3,1)=4×3×3=36;C部门出2人,A、B各出1人,同样36。三种情况互不重叠,按加法原理相加得到54+36+36=126。凡是遇到"每个组至少一个"的题目,先判断哪个组多分了一个,再分类写出组合式,最后相加,三步走基本不会错。
第14题考相邻排列,答案A。3个女生必须相邻,用捆绑法:先把3个女生看成一个整体,与5个男生一起排,共有6个对象,排列数是A(6,6)=720;女生内部还可以交换位置,有A(3,3)=6种;按乘法原理相乘得到720×6=4320。看到"必须相邻"就用捆绑,看到"不能相邻"就用插空,这是排列组合里两个固定套路。写题时先把"谁和谁绑在一起"圈出来,再算整体排列,容易漏掉的是内部排列这一层乘数。
2.3 格雷码与存储单位:背性质,不背码表
第4题考4位格雷码,答案D。格雷码的定义是相邻两个编码之间只有1位不同。验证选项D:0000到0001只有最后一位变化,0001到0011只有倒数第二位变化,0011到0010是最后一位变化,后面0110到0111、0111到0101都满足相邻差1位。选项A最后从0101跳到1000,至少要变3位,直接排除。这个考点不需要背整张格雷码表,只需要拿"相邻码只有1位不同"这个性质逐项检查,哪个选项全程满足就选哪个。
第5题考存储单位换算,答案D。记1KB=1024字节,1MB=1024KB,1字节=8位,那么1MB=1024×1024×8=8388608位。这里有两个坑:一是有人把1024近似成1000,算出8000000;二是有人只算到字节数1048576,忘了乘8。换算题建议分两步写:先算字节数,再算位数,每一步写清楚单位,基本不会错。CSP-J几乎每年都会在存储单位、进制、补码范围这三类基础题中轮流出题,这几分属于必须拿满的。
2.4 C++语言与操作系统常识:考得细但都来自课本
第6题选C。C++的基本数据类型是int、float、char、bool这些,struct是复合数据类型,由基本数据类型组合而成,不是基本类型。第7题选D。C++的循环语句只有for、while、do-while三种,repeat-until是Pascal风格的语法,不在C++里。这类"以下哪个不是"的题,陷阱就是把别的语言的语法混进来,复习时把C++的运算符、控制语句、数据类型列表过一遍就能应对。
第8题考char运算,答案B。C/C++中字符是连续编码的,'a'+13表示从'a'往后数13个字母。从a开始数:a是第0个,b是第1个,c是第2个,一直数到第13个是n,所以(char)('a'+13)等于'n'。第15题考编译器作用,答案B。编译器把源代码转换成机器代码,注意"直接执行源代码"是解释器的事情,"管理程序运行时的内存"是操作系统的职责,这几个概念在初赛里经常互换干扰。
2.5 算法数据结构四件套:二分、图论、二叉树、栈
第9题答案是10。1000个元素的有序表用二分查找,最多比较次数是⌈log2 1000⌉,因为2^9=512,2^10=1024,1000夹在中间,所以需要10次。这个考点可以记成公式:最大比较次数=⌊log2 N⌋+1,N=1000时⌊log2 1000⌋=9,加1得到10。
第11题考图论基础,答案B。无向图中每条边连接两个顶点,贡献2个度,所以所有顶点的度数之和等于边数的两倍。这个结论在判断图的性质、计算可遍历性时经常用到。
第12题考二叉树遍历,答案A。前序遍历是[A,B,D,E,C,F,G],中序遍历是[D,B,E,A,F,C,G]。从前序可知根是A,看中序,A左边的是左子树[D,B,E],右边的是右子树[F,C,G]。左子树的前序是[B,D,E],所以B是左子树的根,再看中序D在B左边、E在B右边,确定D是B的左孩子、E是B的右孩子。右子树同理,C是根,F和G分别是左右孩子。最后按后序遍历的"左右根"顺序写出[D,E,B,F,G,C,A]。
第13题考栈的出栈合法性,答案D。模拟一遍选项D的序列1,3,5,2,4,6:1入栈后出栈得到1;然后2入栈、3入栈、3出栈得到3;再4入栈、5入栈、5出栈得到5;此时栈里从底到顶是2、4,下一个要出的是2,但栈顶是4,必须先出4才能出2,所以这个序列不可能。验证栈序列的通用方法是:每遇到一个输出x,就不断地把输入序列的下一个元素入栈,直到x出现在栈顶再弹出;如果x在栈中但不在栈顶,这个序列就非法。
3. 阅读程序高频考点:素数、爬楼梯DP、递归函数的读题模型
3.1 素数计数与求和:改循环边界不等于改结果
阅读程序第一题的核心是三个函数:isPrime判断素数,countPrimes统计2到n之间的素数个数,sumPrimes求这些素数的和。isPrime的关键代码是for(int i=2;i*i<=n;i++),枚举到根号n就能判断是否有因子,这是素数判断的标准写法。
bool isPrime(int n) { if (n <= 1) return false; for (int i = 2; i * i <= n; i++) { if (n % i == 0) return false; } return true; } int countPrimes(int n) { int count = 0; for (int i = 2; i <= n; i++) { if (isPrime(i)) count++; } return count; }第16题判断输入10时第一个输出4、第二个输出17是否正确。10以内的素数是2、3、5、7,共4个,和是17,所以判断为真。这道题只要会列素数表就能拿分。
第17题是整份卷子里最容易错的判断题之一。题目说把isPrime里的循环条件改成i<=n/2,输入20时countPrimes(20)的输出会变成6。这个说法是错误的。原因在于i*i<=n和i<=n/2都是合法的枚举上界,前者是√n,后者是n/2,n/2比√n更大,枚举更充分,不会漏掉任何因子,也不会多判出错。所以countPrimes(20)的结果仍然是8,不是6。这个题的坑在于:很多考生看到循环条件变了就认为结果必然变化,没有去想"更大范围是否改变判定结果"。读程序题时,改动循环边界要先判断是否影响函数语义,再判断是否影响输出。
第19题求输入50时sumPrimes的输出,答案B。把50以内素数全部列出:2、3、5、7、11、13、17、19、23、29、31、37、41、43、47,求和得到328。这类求和题没有捷径,平时练熟100以内的素数表,考试时列出来逐个加就行。
3.2 最小花费爬楼梯:从转移式反推dp表
阅读程序第二题从题干给出的转移式可以看出,它是最小花费爬楼梯的动态规划模型。题干没有给完整代码,但dp[i]=min(dp[i-1],dp[i-2])+cost[i-1]或cost[i]这种形式是标准写法。按题目反推,代码逻辑等价于:
int minCost(vector<int>& cost) { int n = cost.size(); vector<int> dp(n); dp[0] = cost[0]; dp[1] = cost[1]; for (int i = 2; i < n; i++) { dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i]; } return min(dp[n - 1], dp[n - 2]); }dp[i]的含义是"踩到第i级台阶所需的最小花费"。到达第i级只能从第i-1级或第i-2级过来,所以取两者中较小的花费,再加上当前这一级的费用cost[i]。最终结果取最后两级中的较小值,因为最后可以跨过最后一级。
第21题输入cost数组{10,15,20},输出15。手动填表:dp[0]=10,dp[1]=15,dp[2]=min(15,10)+20=30,最终输出min(30,15)=15,判断为真。第24题输入{1,100,1,1,1,100,1,1,100,1},答案是6。逐项计算:dp[0]=1,dp[1]=100,dp[2]=min(100,1)+1=2,dp[3]=min(2,100)+1=3,dp[4]=min(3,2)+1=3,dp[5]=min(3,3)+100=103,dp[6]=min(103,3)+1=4,dp[7]=min(4,103)+1=5,dp[8]=min(5,4)+100=104,dp[9]=min(104,5)+1=6,最终输出min(6,104)=6。
第26题把转移式改成dp[i-1]+cost[i-2],输入{5,10,15}时输出10。修改后不再是"两条路取最小",而是只能从前一级过来,并且费用取前前级的cost[i-2]。dp[0]=5,dp[1]=10,dp[2]=dp[1]+cost[0]=10+5=15,输出min(15,10)=10。做这类题时,先把dp数组的下标定义写在草稿纸上,再手工填一张小表,就能避开大部分坑。
3.3 customFunction递归:先写递推式,再数调用次数
阅读程序第三题考递归函数。从第30题输入5和4返回625可以反推,customFunction(a,b)的功能等价于求a的b次方。第27题输入2和3时函数返回值应该是2^3=8,不是64,所以判断为假。第28题说b为负数时会陷入无限递归,这是真的,因为递归过程中b每次减1,负数方向永远到不了终止条件。第29题说b越大运行时间越长,也是真的,递归层数随b线性增长。
第30题输入5和4,5^4=625,选B。第31题输入x=3、y=3,最终输出是144。注意题目问的是"程序的最终输出",不是customFunction(3,3)单次调用的返回值。customFunction(3,3)=27,最终输出144说明main里对返回值做了多次调用或组合运算。这类题的通用检查法:在草稿纸上把每次调用的入参和返回值列成表,再顺着main的表达式从左到右代入,少一步都会掉坑。
第32题把函数体改成return a + customFunction(a-1,b-1),输入3和3。改造后的递推式是f(a,b)=a+f(a-1,b-1),展开f(3,3)时先加3再调f(2,2),f(2,2)先加2再调f(1,1),逐层展开到终止条件后,再从最底层把返回值逐层带回。递推展开时容易漏掉某一层的a值或终止条件的返回值,所以每一层都要在纸上写清楚"当前层加几、下一层入参变成什么"。
4. 完善程序高频考点:平方数枚举与汉诺塔递归的填空套路
4.1 判断平方数:五空就是一个枚举查找模板
完善程序第一题是判断正整数n是否为完全平方数。补全后的isSquare函数如下:
bool isSquare(int num) { int i = 1; int bound = (int)floor(sqrt(num)); for (; i <= bound; ++i) { if (num == i * i) { return true; } } return false; }第33题①处填1。题目说存在正整数x,所以枚举从1开始,不能从0开始。第34题②处填(int)floor(sqrt(num))。枚举上界是num的平方根取整,sqrt返回浮点数,先floor向下取整再转int,保证i最多取到√num。如果选sqrt(num/2)-1,会漏掉4这类平方数,因为sqrt(2)取整是1,i取不到2。第35题③处填num==ii。这里必须是双等号判断相等,如果写成num=ii就把num的值改掉了,循环后面的判断全部错乱。第36题④处填true,找到i*i==num就返回真。第37题⑤处填false,整个循环结束都没找到,说明不是平方数。
这类完善程序的读题顺序是:先看函数整体在做什么,再确定枚举起点和枚举上界,最后看返回值的真假分支。五个空对应的就是"起点、终点、条件、命中返回、未命中返回",套这个模板基本不会错。
4.2 汉诺塔:三步递归与参数换位
完善程序第二题是汉诺塔,补全后的dfs函数如下:
void dfs(int i, char src, char tmp, char tgt) { if (i == 1) { move(src, tgt); return; } dfs(i - 1, src, tgt, tmp); move(src, tgt); dfs(i - 1, tmp, src, tgt); }汉诺塔的递归思路只有三步:先把上面i-1个盘子从src借助tgt挪到tmp,再把第i个盘子从src直接挪到tgt,最后把i-1个盘子从tmp借助src挪回tgt。第38题①处填1,只有一个盘子时直接移动,这是递归出口。第39题②处填src,tgt,即move(src,tgt)。注意这里不是src,tmp,因为当前这一步就是把最大的盘子挪到目标柱。第40题③处填src,tgt,tmp,第一次递归的参数顺序是"源、目标、中转",让第i-1个盘子全部先到临时柱。第41题④处填tmp,src,tgt,第二次递归的参数顺序是"中转、源、目标",让盘子从临时柱回到目标柱。第42题⑤处填i-1,调用规模每次减1。
这道题最容易错的是③和④的参数顺序正好相反。记忆口诀是:"盘中盘先借道,大盘直接走,再来一盘借道回。"考场上如果拿不准,用n=2手工走一遍验证:dfs(2,A,B,C)先把1个盘子从A经C挪到B,再把第2个盘子从A挪到C,最后把B上的盘子经A挪到C。三步顺序和参数一一对应,验证完再填答案。
5. 常见易错点与失分点:初赛最容易丢分的五个位置
5.1 int范围记成对称区间
错在哪:第1题在A、B、D之间犹豫,最后选了一个上下界关于0对称的选项。为什么错:脑子里只记得2^31约等于21亿,忽略了补码表示中负数比正数多一个。怎么答对:死记"下界绝对值比上界大1",32位int下界是-2147483648,上界是2147483647。考试时遇到这个考点,直接把-2^31到2^31-1默写出来再对选项。
5.2 混合进制没有统一成十进制
错在哪:第2题把148当成十进制14或18来算,把D16当成字母D忽略掉,导致整个式子算错。为什么错:不同进制混在同一个表达式里时,每一位的权值不同,不能直接按十进制加减。怎么答对:看到八进制、十六进制、二进制混算,第一步全部转十进制,第二步再代入计算。八进制转十进制按权展开,十六进制先记住A到F对应10到15,二进制从低位开始每一位对应1、2、4、8。
5.3 存储单位换算漏掉bit与byte的8倍关系
错在哪:第5题算出1048576或8000000,前者只换算到字节数,后者把1024近似成1000。为什么错:1MB到KB再到B都是1024倍,B到bit还要乘8,任何一步漏掉都会错。怎么答对:写清楚链路1MB=1024KB=1024×1024B=1024×1024×8bit,分步计算,每一步标注单位,最后结果8388608。
5.4 二分查找次数该向上取整却向下取整
错在哪:第9题算出log2 1000约9.97,部分考生取9就选了7次。为什么错:二分查找最多比较次数需要保证覆盖所有元素,比较次数必须是整数且要"够用",所以要向上取整。怎么答对:用2的幂判断,2^9=512不够覆盖1000,2^10=1024才够,所以是10次。这类题不要直接看小数,而是找相邻的2的幂。
5.5 栈出栈序列只凭直觉没做完整模拟
错在哪:第13题看到1,3,5,2,4,6觉得大小交错很有规律,选成"可能"。为什么错:没有模拟到5出栈之后栈里还剩2和4,且4在2上面。栈是后进先出,4没出栈前2不可能先出。怎么答对:每验证一个出栈序列,就手动模拟入栈和出栈全过程,当输出x时,持续入栈直到x在栈顶;如果x已经入栈但被压在下面,直接判定非法。把ABCD四个序列全部模拟一遍,宁慢勿错。
6. 答题技巧:用代入、填表、边界检查三招稳住初赛成绩
先按模块分配时间。2024年初赛总分100分,建议单选30分控制在25到30分钟内完成,阅读程序40分用40分钟,完善程序30分用25分钟,剩余时间全部留给检查。如果一道题卡了超过5分钟,先标记跳过去,初赛判断题和选择题对错各占一半分值,卡题不如先保后面的代码题。
| 模块 | 分值 | 建议用时 |
|---|---|---|
| 单项选择题 | 30分 | 25到30分钟 |
| 阅读程序 | 40分 | 40分钟 |
| 完善程序 | 30分 | 25分钟 |
| 检查 | 25分 | 20到25分钟 |
读阅读程序题时,第一招是小数据代入。拿题目给的输入n=10或更小的样例手算一遍,和代码输出对比,能快速发现理解偏差。第二招是填表,凡是遇到dp或递归,就在草稿纸上画一张状态表,dp题填dp[0]到dp[n]的值,递归题列每次调用的入参和返回值,把抽象执行变成具体数值。第三招是边界检查,n=0、n=1、负数、空数组都要单独想一遍,很多程序题把边界藏在if(n<=1)这种分支里,完善程序填空也经常在终止条件处设空。
从那以后,我每次带学生练真题,都强制先走一遍"代入、填表、边界"这三步,再长的程序也不凭感觉直接选。初赛的坑其实很固定,踩过一次长记性,比刷十套题都管用。希望帮到你。
本文还有配套的精品资源,点击获取