ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

信息学竞赛预选赛备考:C++基础语法与程序阅读全解析

信息学竞赛预选赛备考:C++基础语法与程序阅读全解析

简介:这是2024年海淀区中小学生信息学竞赛校级预选赛试卷,面向参赛中小学生及辅导教师,可用于考前模拟与知识查漏。试卷由编程基础知识单选题和程序阅读单选题组成,前者覆盖变量命名合法性、赋值语句写法、二进制与十进制转换、表达式运算结果、逻辑判断、函数定义与递归、运算符特性、for/while循环及break/continue作用等基础内容;后者给出多段C++代码,要求推断输出结果或变量取值,涉及素数判定、约数统计、最大公约数、回文数判断、筛法等经典算法片段,适合训练代码阅读与逻辑推演能力,也能帮助考生快速定位知识薄弱环节。试题以单个PDF文档形式打包,共422KB,下载后可直接打印或屏幕阅读。目前已有576人学习使用,可作为学生参加海淀区信息学竞赛的针对性练习材料。对希望了解海淀区信息学竞赛命题风格、检验自身编程基础的学生而言,这份试卷提供了方便的入门训练入口。

1. 海淀区信息学竞赛的校级预选赛:一张试卷卡在编程竞赛的入口

考驾照得先过科目一:坐在电脑前答交规题,一道题都不用碰车。海淀区中小学生信息学竞赛的校级预选赛,干的就是这件事——它是整条竞赛路的第一道闸门,过了它才有资格进区级初赛,再往后才是真正上机写代码的复赛。

这张试卷有一个鲜明特点:几乎全是选择题。它不要求你打开编译器敲出一段能运行的程序,而是把编程基础知识和程序片段摆在卷面上,让你判断对错、推断输出、算出结果。一句话,它考的不是"会不会写",而是"懂不懂"。

对想走信息学竞赛这条路的学生和家长来说,这份试卷值得仔细研究。预选赛的分数直接决定你有没有下一轮的入场券,而它的考法和复赛上机完全不同。把这张试卷吃透,等于先把竞赛入口处的规则摸清了。

2. 为什么要有校级预选赛:一场编程竞赛为什么先设笔试关

2.1 报名的人太多,上机考场装不下

海淀区信息学竞赛并非谁都能报名。试卷第一题就点明了报名规则:报名在网上报名系统进行,必须经过学籍所在学校的指导教师审核。这里的关键词是"学籍所在学校"——参赛资格和学籍绑定,学生在哪里读书,就从哪所学校的通道报名。这和社会上的自由报名考级完全不同,它天然圈定了参赛范围。

但即便有学籍门槛,海淀区中小学数量多、编程热度高,最终报名人数还是会远超一场上机考试能容纳的量级。上机编程考试要占用机房,每个考生一台电脑,监考老师要来回走动,判题要么靠人工看代码,要么靠判题系统比对输出。场地、设备、人力、时间,每一项都是成本。

校级预选赛把这些成本压到了最低:一张纸质试卷、一支笔、一个考场,几百名学生同时答题。选择题可以快速核对,一天之内就能完成一轮筛选。这和驾照科目一的逻辑一模一样——先用最便宜的方式,把"完全没摸过车"的人筛掉,再把宝贵的练车和路考资源留给真正有准备的人。

2.2 晋级路径:每一层都在换一种考法

从报名到最终拿奖,海淀区信息学竞赛的流程大致是:网上报名系统报名,学校指导教师审核资格;然后参加校级预选赛,达到一定分数晋级区级初赛;区级初赛再筛一轮,达到分数线进入区级复赛;复赛才是真正的上机编程考试。

注意每一层的考法都在变化。校级预选赛是笔试,考基础知识和阅读代码;区级初赛的题型和预选赛类似,但难度和分数线要看当年的竞争情况;到了复赛直接打开电脑写代码,题目通常是完整的算法题,需要自己设计数据结构和算法,用程序解决问题。

这提醒参赛者一个关键信息:预选赛的备考方式和复赛完全不同。预选赛重"识记和理解",复赛重"设计和实现"。很多学生在预选赛阶段拼命背代码模板,结果复赛面对一个没见过的问题直接懵住,就是因为没意识到预选赛只是门槛,不是终点。

2.3 三种题型对应三种能力

这套预选赛试卷的大结构很清晰,三大题型各有分工。

第一大题是编程基础知识单选题,像一张"语法体检表"。它把变量命名规则、赋值语句写法、二进制与十进制的转换、表达式运算结果、逻辑表达式、函数特性、循环语句、二维数组初始化等知识点挨个考一遍。这些内容不涉及复杂算法,纯粹看你有没有把 C++ 的基础规矩记牢。比如它会问"合法变量名称是哪个",或者"把字符串赋值给整型变量为什么错",这些都是初学者第一次写代码时最容易踩的坑。

第二大题是程序阅读单选题,每题给出一小段完整的 C++ 程序,让你推断输入某个整数后的输出。例如有程序用 for 循环统计一个数的约数个数,有程序用埃氏筛标记素数,有程序用递归求最大公约数,还有程序判断回文数。你没法把代码复制到编译器里跑,只能在大脑里模拟执行,一句一句地推。这考的就是"人肉编译器"的水平。

第三大题是问题阅读单选题,也叫应用题。它用大段文字描述现实场景:手机桌面上应用程序图标按规则交换位置、视频服务器按上传时间排队压缩视频、多枚异形骰子同时掷出求和、小猴爬山连续多天的海拔变化。题目不给你代码,只给你规则和条件,你要自己把文字描述翻译成可计算的逻辑模型。这种建模能力,恰恰是信息学竞赛复赛上机题的核心考察点。

所以说,校级预选赛虽然只是"科目一",但它用三种不同的题型,把"懂语法、能读码、会建模"三个层次的能力一次性测了一遍。通过的人,至少说明在思维上已经迈进了编程的门。

3. 编程基础知识单选题:一场对 C++ 语法规矩的全面体检

3.1 变量命名与赋值:起名字的规矩和放东西的方式

变量名是程序里给数据起的名字,但这个名字不是随便起的。C++ 的规则是:只能以字母或下划线开头,后面可以跟字母、数字或下划线;不能和关键字重名;不能包含空格和标点。

int 2x; // 错误:不能以数字开头 int _x; // 正确:下划线开头合法 int double; // 错误:double 是关键字,不能用 int x y; // 错误:变量名里不能有空格

赋值语句的考点同样围绕"规矩"展开。a = 1;是标准赋值,把整数 1 放进变量 a 的格子里。而a = "1";一定错误——左边是整型变量,右边是字符串,类型不匹配。容易混淆的是a = '1';,这个其实是合法的:C++ 里字符 '1' 会被自动转成它的 ASCII 码值 49,然后赋给整型变量 a。所以试卷里问"哪个赋值写法一定错误",答案通常就是字符串赋值给整型变量那项。

另外要分清赋值和比较:a = 1是赋值,a == 1是比较。写代码时一个等号和两个等号搞混,编译器不会直接报错,但程序逻辑会跑偏,这种翻车在初学者里太常见了。

3.2 进制转换与表达式运算:数学功底决定上限

二进制转十进制,考的是位权展开。比如二进制数 1101,从右往左每一位的权重是 1、2、4、8,所以 1101 转十进制就是 1×8 + 1×4 + 0×2 + 1×1 = 13。这套规则是理解计算机内部数据的基础,也是不少学生的第一道坎——平时数数都用十进制,突然要换一种进制,脑子转不过弯。

表达式运算题考的是运算符优先级和求值顺序。优先级从高到低大致是:逻辑非!> 算术运算符 > 关系运算符 > 逻辑与&&> 逻辑或||。遇到一个混合表达式,先算谁后算谁决定了最终结果是真是假。

这里还有一个隐藏考点:字符型变量能不能参加算术运算。很多初学者以为 char 是"字母",不能算数。实际上 C++ 里 char 的本质是一个 0 到 127 的整数,用的是 ASCII 编码。字符 'a' 的 ASCII 值是 97,所以'a' + 1的结果是 98,对应字符 'b'。字符完全可以参加算术运算,试卷里"字符型变量不能参加算术运算"这个说法就是错的。想明白这一点,字符和整数之间的转换题就能一眼看穿。

3.3 循环与跳转:让程序"重复做事"的三种手段

for 和 while 是 C++ 里最常见的两种循环。试卷里有道题说"for 语句能够实现确定次数的循环功能",还有一句"while 语句专用来实现不确定次数的循环功能"。前者是对的,后者是错的。

为什么错?因为 for 和 while 本质上可以互相转换。for 把初始化、循环条件、步进写在一行,适合"数着次数"的计数循环;while 只写循环条件,适合"等到某个状态发生"的场景。但"适合"不代表"只能"。用 while 完全能写确定次数的循环,用 for 也完全可以写不确定次数的循环,只是写起来别扭而已。把"风格偏好"说成"功能限制",就是这道题的陷阱。

循环里还有两个跳转语句需要分清。break 是彻底跳出整个循环,循环直接结束;continue 是跳过本次循环的剩余语句,回到循环条件判断,开始下一轮。打个比方:break 是直接踢门出去,continue 是这轮不干了、下轮接着排队。考试里这两句话经常一字之差,读错一个词,整个程序的执行过程就全变了。

3.4 函数与数组:模块化和数据箱子

函数部分考的是基本特性:每个程序有且只有一个主函数,主函数是程序执行的入口;函数可以自己调用自己,这就是递归;递归函数就是函数自己调用自己的典型代表。

这里最容易混的是"嵌套"这两个字。C++ 里函数定义不能嵌套——你不能在一个函数里面再完整地定义另一个函数;但函数调用可以嵌套,一个函数调用另一个函数,调来调去都合法。试卷里"函数不支持嵌套"这个说法之所以是错的,就是因为它把"不能嵌套定义"和"不能嵌套调用"混为一谈。

二维数组是另一个高频考点。int a[3][2] = {2,3,4,5,6,7};表示一个 3 行 2 列的二维数组,初始化数据按行填充:

下标列0列1
行0a[0][0]=2a[0][1]=3
行1a[1][0]=4a[1][1]=5
行2a[2][0]=6a[2][1]=7

C++ 数组下标一律从 0 开始,这是最容易翻车的地方。a[1][1] 对应的是第 2 行第 2 列,值是 5;而 a[2][2] 已经越界了——数组只有 0、1、2 三行,每行只有 0、1 两列,a[2][2] 根本不存在。题目就是抓这种"看着顺眼、实际越界"的陷阱。

4. 程序阅读题:在脑子里运行一遍代码的能力

4.1 人肉编译器:用一张纸追踪变量

程序阅读题没有捷径,核心方法就一个:拿一个具体输入,逐行执行代码,用纸笔记录每个变量的变化。这不是玄学,而是实打实的"人肉模拟执行"。

以试卷里的约数计数题为例:

int n, cnt = 0; cin >> n; for(int i = 1; i < n; i++) { if(n % i == 0) cnt++; } cout << cnt;

这个程序统计小于 n 的约数个数。输入 n = 10,循环从 i = 1 跑到 i = 9。10 能被 1、2、5 整除,所以 cnt 最终是 3。注意循环条件是i < n而不是i <= n,因此 10 本身不会被统计。如果条件改成i <= n,答案就变成 4。一个小于号的差别,输出完全不一样。

这就是程序阅读题的本质:把代码当成一份"操作说明书",在人脑里一步步执行。变量就是便签纸上的格子,循环就是反复执行同一段操作,函数调用就是跳到另一份说明书再跳回来。

提示:程序阅读题追求一次算对,而不是算快。宁可多花两分钟在草稿纸上画变量表,也别凭感觉选答案。

4.2 高频算法原型:筛法、递归、回文

这套试卷的程序阅读题,表面上是四五个独立的小程序,实际上反复在考几个经典算法原型。认识这些原型,读题速度会快很多。

埃氏筛(素数筛):

int n, a[10010]; cin >> n; a[1] = 1; // 1 不是素数,先标记 for(int i = 2; i <= 10000; i++) { if(a[i] != 0) continue; // 已经被标记过就跳过 for(int j = 2; j <= 10000 / i; j++) { a[i * j] = 1; // 把 i 的倍数标记为合数 } }

这段代码里,a[i] 等于 0 表示 i 是素数,等于 1 表示合数。外层循环找到第一个没被标记的数,它就是素数;内层循环把它所有的倍数都标记成合数。筛法的时间复杂度接近线性,是信息学竞赛入门必会的内容。

求最大公约数的递归版本:

int f(int a, int b) { if(b == 0) return a; return f(b, a % b); // 辗转相除 }

这就是欧几里得算法。只要 b 不是 0,就用 a 对 b 取模,然后递归调用。当 b 变成 0 时,a 就是最大公约数。这段代码短,但信息量大:它同时考了递归、取模运算、函数返回值三个知识点。

回文数判断:

bool f(int x) { int s = 0, t = x; while(t != 0) { s = s * 10 + (t % 10); // 取出个位,拼到结果末尾 t /= 10; // 去掉个位 } return (s == x); // 倒过来相等就是回文数 }

核心是s = s * 10 + (t % 10)这一句:每次从原数末尾取出一个数字,拼到新数的末尾。整个过程就是把数字倒过来写。循环结束之后比较 s 和 x,相等说明是回文数。这个"数字反转"的写法在很多题目里都会出现。

4.3 分支逻辑题:素性判断与哥德巴赫猜想

试卷里有一道程序用到了一个经典数论逻辑。它先判断输入的数是不是素数,然后根据情况输出 1、2 或 3:

bool f(int x) { for(int i = 2; i * i <= x; i++) { if(x % i == 0) return 0; // 找到约数,不是素数 } return 1; // 没找到约数,是素数 }

这里i * i <= x是一个重要的优化:判断素数只需要试到平方根就够了,因为如果 x 有一个大于平方根的因子,那它一定对应一个小于平方根的因子。这个优化在竞赛里几乎必考。

接下来的分支逻辑是:如果 n 是素数,输出 1;否则如果 n 是偶数,输出 2;否则如果 n-2 是素数,输出 2;否则输出 3。这实际上是简化版的哥德巴赫猜想——任何一个大于 2 的偶数都可以表示成两个素数之和。程序用穷举的方式判断 n 能不能拆成两个素数的和。读懂这个逻辑,比单纯算出答案更有价值,因为它训练的是"把数学猜想翻译成程序判断"的思维。

4.4 长文本应用题:把中文规则翻译成逻辑模型

第三大题的问题阅读题不给代码,只给一大段文字描述,这种题的难点不在编程,而在"翻译"。

比如手机操作系统那道题,描述了应用程序图标在屏幕上如何排列、启动后如何与前一个图标交换位置。题目特别强调了一句:注意区分应用程序的编号以及位置,这是两个不同的变量。这句话就是全题的题眼——很多人翻车,就是因为把"编号"和"位置"当成一回事,一交换就全乱套。

再比如视频压缩问题:若干台服务器按视频上传时间依次压缩,每台每秒压缩一分钟时长的视频,同一时间一台服务器只能处理一个视频,视频到了有服务器空闲就立即开始。这类题本质上是在考"排队论"的模拟——把时间轴画出来,一台服务器一条时间线,把每个视频的到达时刻和压缩时长标上去,一个格子一个格子地推。

这类题的通用做法是三步:第一步,圈出题目里的所有名词和规则;第二步,把规则写成"如果……那么……"的条件句;第三步,拿题目给的具体数据代入,按时间顺序或操作顺序一步步推演。把文字翻译成逻辑模型的能力,正是复赛上机时设计算法的前身。

5. 常见误区与易混概念:新手最容易翻车的几个点

5.1 变量名:看着像单词不代表合法

易混点:_x、2x、x y到底哪个是合法变量名?

为什么混:很多人觉得名字只要"读起来像变量"就行,忽略了语法规则。

怎么区分:记住三条硬规矩——开头只能是字母或下划线,不能是数字;不能和关键字重名;名字中间不能有空格。只要违反其中任何一条,编译器直接报错。考试不会考冷门规则,翻来覆去就是这三条。

5.2 break 和 continue:一个跳出,一个跳过

易混点:break 和 continue 都写在循环体里,都能影响循环流程,好像都"打断"了循环。

为什么混:它们的中文翻译都有"中断"的意思,初学者容易当成一回事。

怎么区分:break 是立刻结束整个循环,跳出循环体,继续执行循环后面的代码;continue 是结束本次循环的当前轮次,跳过本轮剩余语句,回到循环条件判断,开始下一轮。一句话:break 是退场,continue 是跳轮。

5.3 数组下标:从 0 开始是规矩中的规矩

易混点:int a[3][2]里,a[1][1] 到底是第几个元素?

为什么混:日常生活数数从 1 开始,"第一个""第二个"顺口得很,程序里却从 0 开始。

怎么区分:C++ 数组下标一律从 0 开始。a[3][2] 是 3 行 2 列,下标范围从 a[0][0] 到 a[2][1]。初始化{2,3,4,5,6,7}按行填充,所以 a[1][1] 的值是 5。更阴险的是 a[2][2]——看着下标 2 很合理,其实已经越界,这是考试最爱挖的坑。

5.4 字符型变量:'1' 和 1 不是一回事

易混点:字符 '1' 和整数 1 长得几乎一样,能不能互换使用?

为什么混:在屏幕上打印出来都是字符 1,容易觉得它们相等。

怎么区分:'1' 是字符,在内存里存的是它的 ASCII 码值 49;1 是整数,存的就是数字 1。两者相差 48,运算结果完全不同。反过来,字符型变量是完全可以参加算术运算的——它本质就是 0 到 127 的整数,'a' + 1 等于 98,对应字符 'b'。把字符当整数用,是 C++ 里一个容易忽略又极其重要的特性。

5.5 循环边界:i < n 和 i <= n 的差别

易混点:统计约数时,n 本身算不算约数?

为什么混:数学课上学过,一个数的约数包含它自己,比如 10 的约数有 1、2、5、10 共 4 个。程序里写i < n就只有 3 个。

怎么区分:看循环条件用的是<还是<=。程序阅读题最阴的地方就在这类边界,一个符号的差别,答案就从 3 变成 4。动手数几个数再下结论,比盯着代码发呆可靠。

6. 这份试卷什么时候用:从备赛到日常的边界判断

6.1 适合用在哪

第一,备赛摸底。比赛前一到两个月,限时做一遍这份试卷,然后看丢分分布:语法题丢分多,回去补基础规则;程序阅读丢分多,专门练脑内模拟执行;应用题丢分多,练文字转逻辑模型的翻译。一套试卷测出三个板块的薄弱点,比盲目刷题高效得多。

第二,知识点自测。学完变量、循环、数组、函数这几个单元之后,拿它当阶段性测验。这份试卷覆盖的知识面和信息学竞赛入门教学大纲高度重合,哪一章没学透,做一遍就现原形。

第三,教师参考。负责组织校级预选赛的老师,可以直接参考这套题的题型分布、难度梯度和考察方式,省去从零设计试卷的时间。

6.2 不适合用在哪

第一,零基础系统学习。这份试卷是"体检"不是"教材"。完全不懂编程的人做它只会受挫,它没有讲解,只有判断对错,学不到新知识。

第二,复赛冲刺。预选赛是选择题,复赛是上机写代码,两者技能要求不同。已经进入复赛备考的学生,应该去刷需要自己写完整程序的题目,而不是反复做这份笔试卷。

第三,算法深度训练。试卷里的算法都是经典原型,属于入门难度。想挑战更高阶的数据结构或动态规划,需要另找专项资料。

预选赛和复赛的定位差异,可以看这张表:

维度校级预选赛区级复赛
形式笔试选择题上机编程
考点语法、读码、建模算法设计与实现
核心能力理解和推断设计和调试
备考方式背规则、练阅读刷题、调错、积累套路

带 A 同学备赛那一年,我让他先做了一遍这份预选赛试卷当摸底。他丢分最狠的不是语法,而是程序阅读——一看到 for 循环就晕,变量一多就乱。我们花了一周时间专门练脑内模拟,把每个变量画成表格,一行代码一行代码地推,硬生生把阅读正确率提了上来,后来他顺利过了预选赛。从那以后,我带每个学生备赛都强制先来一遍这样的笔试摸底,先把问题找准再谈其他。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进