每年CSP-J报名通道一打开就有不少家长和选手来问我同一个问题第一轮笔试到底怎么准备甚至有的孩子平时能把复赛题AC初赛却挂在选择题上原因很反直觉——CSP-J第一轮不是考“会不会写代码”而是考“是否真正理解计算机”。试卷里除了C语法、算法和数据结构还有大量进制转换、逻辑运算、网络协议、存储单位这些看似“与编程无关”的基础知识。这篇文章就是把近年第一轮认证涉及的知识点收拢成一份可执行的基础复习清单覆盖计算机通识、C语言基础、数据结构、算法思维、阅读程序与完善程序的解题方法并附上最后30天的备考时间线。不管你是刚开始准备的小白还是已经刷过几套真题想查漏补缺的选手都能按章节找到自己的薄弱点。1. 第一轮认证的考试规则与命题逻辑先听懂“游戏规则”再上场1.1 别把初赛当成复赛的“低配版”题型差异决定复习方向很多第一次参加CSP-J的同学都会犯一个战略上的错误按照复赛的机试方式准备初赛狂刷代码题可打开第一轮试卷后傻眼了——满眼都是“以下哪个说法正确”“这段程序输出什么”“请选择缺失的代码”。初赛是笔试考的是“解释代码”和“理解概念”的能力而复赛是上机考的是“从零构建解决方案”的能力。这两种能力显然相关但不完全等价。一个能把背包问题写得很熟的人未必能在试题上判断char和int的字节数、推导一段陌生递归的输出、补全一个中途断掉的循环。因此准备第一轮的第一步是先转换心态把自己当成一个“计算机概念与程序的解释器”。你不需要在考场上写出完整程序但需要能看清任何一段程序的执行顺序、变量变化和输出结果。这也带来了一个好消息初赛不依赖编译器不依赖调试环境只要训练过手动模拟完全可以在试卷上把程序“跑”出来。换句话说初赛的分数是可以靠有针对性的训练稳定提高的不存在“运气流”。1.2 近几年真题的命题风向通识稳定程序题越来越抠细节从我接触到的近几年真题来看第一轮笔试的总体风格比较稳定没有太多偏题怪题。单选题会固定考察进制转换、逻辑运算、存储单位、网络基础、数据结构性质和简单算法分析这些题目大多直接对应教材上的标准知识点关键是熟练度。相比之下阅读程序和程序完善题的难度在逐年增加命题人越来越喜欢在代码里加入“边界条件”和“副作用”一类的细节比如循环变量从0还是从1开始、比较时用还是、递归终止条件是否漏掉0这些地方恰恰是区分度所在。还有个很明显的变化现在的阅读程序题不再只要求“写输出结果”还会以判断题或选择题的形式让你判断“执行到某一行时某个变量的值”。这比单纯写输出更难因为出题人可以直接瞄准你最可能混淆的一个中间状态。针对这种趋势平时练习时要养成“全程记录变量”的习惯不要只关注最后的输出。考场上你能在草稿纸上完整还原一段程序的“运行录像”就已经赢下大半。2. 计算机通识基础不碰代码也能稳定拿分但陷阱都藏在细节里2.1 进制转换与编码年年必考练到“肌肉记忆”为止进制转换是第一轮笔试的“钉子户”几乎每年都会有2到3道题直接或间接涉及。要掌握的是二进制、八进制、十进制、十六进制之间的互相转换以及小数部分的处理。整数转换可以用“按权展开”或“短除法”小数部分则需要“乘2取整”或“乘16取整”。比如二进制小数0.101转换成十进制1*2^(-1) 0*2^(-2) 1*2^(-3)0.50.1250.625。注意二进制小数可能无法精确表示所有十进制小数比如0.1在计算机里是一个无限循环二进制数这是考察“浮点数误差”时的常见背景。编码方面需要区分ASCII、Unicode和UTF-8。ASCII用7位或8位表示128/256个字符数字0的ASCII码是48大写A是65小写a是97这三个基准数字必须记住。Unicode是字符集给每个字符一个码点UTF-8是编码方案把码点变成字节序列。很多选择题会把“字符集”和“编码方案”混在一起考理解了“字符表和编码规则”的区别就不会被绕进去。概念作用举例字符集给字符分配唯一编号ASCII、Unicode编码方案将编号存储为字节序列UTF-8另外带符号整数要以补码形式理解。初赛中常出现类似“-8的二进制补码表示”这类题解法是正数的补码与原码相同负数补码是“符号位不变其余位取反再加1”。例如8位补码中-1是11111111-128是10000000。把二进制11111111理解成-1而不是255是初学者最容易犯的错。平时可以自己列一张“8位补码表”从00000000一直写到11111111练熟了再看到补码题就不会心里发慌。2.2 存储单位、计算机组成与操作系统理解层次比死记硬背更重要存储单位的换算规则很特别1K 2^10 1024不是1000。1KB 1024B1MB 1024KB1GB 1024MB1TB 1024GB。考试偶尔会给出某个文件的大小让你估算能存多少张图片或多少行文本这时要注意题目有没有强调“按十进制近似”不要一上来就默认1024。字节Byte由8个比特bit组成信息的最小单位是bit但存储的基本单位是Byte。计算机组成要分清“运算器、控制器、存储器、输入设备、输出设备”这五大部分在冯·诺依曼体系中的作用。CPU内部除了运算器和控制器还有寄存器组和高速缓存Cache寄存器存取速度最快、容量最小内存储器分为随机存取存储器RAM和只读存储器ROMRAM断电丢失程序和数据ROM断电不消失但也不能随意改写。凡是对应“当前正在运行的程序和数据”都存在内存中而不是硬盘。可以用一个类比寄存器是手边的工作台Cache是随手能拿到工具的小抽屉内存是书架硬盘是家里的仓库。操作系统部分最常考的是进程与线程、文件与目录、权限位。进程是程序运行的实例线程是进程内的执行流同一进程的多个线程共享内存空间不同进程的内存相互隔离。路径描述中要分清绝对路径与相对路径“.”表示当前目录“..”表示父目录。网络基础则以TCP/IP分层模型和简单网络命令为主IP地址负责定位主机端口号负责区分应用DNS负责把域名解析为IP地址HTTP/HTTPS传输网页数据。做题时不用死背每一层协议的名字抓住“从数据链路层到应用层逐层封装”的思路很多网络题都能推理出来。3. C语言与数据结构把“看不见的代码”变成“看得见的规律”3.1 C语法高频考点作用域、数组、函数与指针C是第一轮笔试默认的主流语言选择题很爱考“读程序”时必须具备的语法细节。高频考点包括变量作用域、数组下标、函数传参方式、运算符优先级、自增自减的副作用、sizeof的返回值。比如sizeof(char)在很多题目中按1字节sizeof(int)在C标准中没有规定必须为4字节但在常见竞赛环境下通常按4字节处理考试也默认这样。遇到int a[10]a[10]是越界访问不是一个合法元素这种“差一”错误在代码追踪题中非常典型。运算符优先级是一个重灾区。x y实际是(x) y后缀自增先返回旧值再自增x y则是先自增再使用新值。如果表达式里连续出现多个自增比如c a a在C里这是一种未定义行为考试的标准答案通常会避开这种题目但你要能识别出这是“不应该出现在良好代码中的写法”。还有逻辑运算的短路机制a b在a为假时不会计算ba || b在a为真时不会计算b利用短路求值可以避免一些函数调用也是阅读程序题的常客。函数部分要区分“传值”和“传引用”。传值时形参是实参的一份拷贝函数内改变形参不会影响实参传引用时形参是实参的别名函数内修改会影响实参。数组作为函数参数时会退化为指针所以修改数组元素会影响原来的数组。递归函数则需要特别关注“出口”和“栈深度”一个没有边界的递归最终会栈溢出。int f(int n) { if (n 1) return 1; return n * f(n - 1); }调用f(5)的结果是120如果把终止条件写反程序就会无限递归。对这种“一眼看懂”的递归考场上真正值钱的是能算出“调用f(4)的过程中f(0)被调用了多少次”这类计数问题。练习时可以手动画出递归调用树把每个节点的返回值标在旁边这个动作对阅读程序题极其有用。3.2 数据结构栈、队列、链表、树与图的“初赛考法”栈和队列在笔试中经常以“入栈顺序与出栈顺序”“后缀表达式求值”“括号匹配”“BFS与DFS的应用场景”等形式出现。栈是后进先出队列是先进先出初赛一般不会要求你手写实现但会问“依次入栈1、2、3不可能的出栈顺序是”这类题要构造一个合法模拟序列来验证。后缀表达式转中缀也是常见题方法是遇到数字压栈遇到运算符弹出两个操作数计算后压回。别在考场上临时画完整的栈代码直接画栈变化的表又快又不容易错。链表和数组不同链表通过指针把不连续的存储单元串起来插入和删除不需要移动元素但随机访问必须从头遍历。选择题常问“在长度为n的单链表头部插入节点的时间复杂度”答案是O(1)在尾部插入则要先找到尾节点是O(n)。二叉树是数据结构的核心性质要记住深度为h的二叉树最多有2^h - 1个节点根深度设为1第i层最多有2^(i-1)个节点对任意非空二叉树叶子节点数等于度为2的节点数加1。前序、中序、后序遍历对应递归访问顺序已知中序和前序可以唯一确定一棵二叉树注意前提是节点值互不重复。图的部分重点是存储方式和遍历。邻接矩阵用二维数组空间复杂度O(n^2)适合稠密图邻接表用数组加链表空间复杂度O(ne)适合稀疏图。深度优先搜索DFS借助系统栈或手工栈广度优先搜索BFS借助队列。初赛常给一个图问你从某点出发的DFS/BFS访问顺序模拟时要特别注意“访问过标记”的时机这个“何时标记”决定了会不会重复访问。建议多画几个不同形状的图练手特别是带环的图。4. 算法思维初赛真正想筛选的底层能力4.1 递推、递归与分治从斐波那契到归并排序算法思维是初赛区分度最高的部分但不要被“算法”两个字吓到。递推是基础用已知项推导未知项例如斐波那契数列f(1)1, f(2)1, f(n)f(n-1)f(n-2)爬楼梯问题本质也是这个关系。递归是递推的代码实现分治是“把一个规模较大的问题拆成若干个结构相同的小问题分别求解后合并结果”。归并排序就是分治的经典案例不断把数组一分为二排序两个子数组再线性合并时间复杂度O(nlogn)。笔试不会让你从零设计归并但常考“归并排序过程中某一趟结束后的数组状态”需要能手动模拟每一趟的合并结果。分治复杂度公式要会套用T(n) aT(n/b) f(n)。归并排序对应a2, b2, f(n)O(n)解出复杂度O(nlogn)。有些同学喜欢直接背复杂度结论但初赛真正考的是“能否在1分钟里判断某段代码属于哪种算法”。看到“分成两个规模减半的问题再合并”就是分治看到“依次取局部最优解”就是贪心看到“每个状态只计算一次并记录”就是动态规划。先把大类分清楚再谈细节这样至少能排除掉一半的错误选项。4.2 排序与复杂度背结论只能拿基础分理解过程才能拿全分排序是所有算法题的地基。需要掌握冒泡排序、选择排序、插入排序、归并排序、快速排序、堆排序这几种的代码思路、时间和空间复杂度、稳定性。考试爱问“哪两个排序是稳定的”“快速排序最坏情况发生在什么情况下”“堆排序建堆的时间复杂度”。我建议把复杂度表、稳定性表都自己做一遍然后手写模拟一轮排序而不仅仅是背表。排序算法平均时间最坏时间稳定空间冒泡排序O(n^2)O(n^2)稳定O(1)选择排序O(n^2)O(n^2)不稳定O(1)插入排序O(n^2)O(n^2)稳定O(1)归并排序O(nlogn)O(nlogn)稳定O(n)快速排序O(nlogn)O(n^2)不稳定O(logn)同时要知道表外的判断插入排序在基本有序时接近O(n)快速排序每次划分都选到极值元素时最坏O(n^2)归并排序即使完全逆序也是O(nlogn)。二分查找的时间复杂度O(logn)前提是数据有序且能随机访问链表上做二分并不高效因为每次都要遍历。贪心和动态规划不需要掌握到复赛程度但基础的“选择策略”要理解。比如找零钱问题能用贪心是有条件限制的不能默认任意面值组合都成立0-1背包问题的“每种物品只能选一次”对应二维DP状态转移初赛完善程序题有概率出现。遇到这类题时先判断“后效性”如果后面的决策不受前面决策影响优先考虑贪心如果子问题重叠则用动态规划。这个判断过程本身也是初赛想要考察的算法素养。5. 阅读程序与完善程序决定“过线”的关键战场5.1 阅读程序的三步推演法变量表、分支覆盖、递归展开阅读程序题占分很重也是很多选手觉得“时间不够用”的板块。我的建议是采用“三步推演法”。第一步在草稿纸上画一张变量表记录每个变量在每一轮循环或递归调用后的值不要边读边在脑子里算第二步遇到分支和循环时把执行路径写成“步骤序列”比如“i3时进入ifa变为2i4时不进入if”避免凭印象跳过某个分支第三步遇到递归时直接画出调用树而不是尝试在大脑中模拟整个递归栈调用树的每个节点就是一个函数调用最后自底向上汇总返回值。来看一个非常常见的片段int a 0, b 1; for (int i 0; i 5; i) { int t a b; a b; b t; } cout b;用变量表推一遍初始a0,b1第一圈i0t1a1b1第二圈i1t2a1b2第三圈i2t3a2b3第四圈i3t5a3b5第五圈i4t8a5b8输出8。这个程序本质上在算斐波那契数列的第6项但若没画表许多同学会在i的结束条件上犯错误以为输出5。阅读程序常见陷阱还包括与的差一错误、静态变量或全局变量被重复使用、函数内部局部变量遮蔽全局变量、字符串比较按ASCII顺序、浮点数相等判断不成立等。平时做题时把错误原因记录在题号旁边考前翻一遍比自己重新做十道题更有价值。我认识不少高分选手都靠“错题原因一句话”这个习惯维持状态。5.2 完善程序空格位置即考点从“答案驱动”转向“逻辑驱动”完善程序题才是最需要“逻辑闭环”的板块。它通常会给你一段带空格的算法代码和一段题目描述选项往往围绕边界条件展开比如“A. i n B. i n C. i n D. i n”这种区分。做这种题先别急着看选项先把题目对应的算法想明白这个变量是前缀和、计数器、最大值还是指针位置再根据空位前后的代码判断这个空位的“职责”是初始化、更新、判断还是递归出口。以二分查找为例核心逻辑是int l 0, r n - 1; while (l r) { int mid (l r) / 2; if (a[mid] x) return mid; if (a[mid] x) l mid 1; else r mid - 1; }如果题目挖去l mid 1或r mid - 1很多同学会填反导致死循环或永远找不到目标。验证方法很简单用长度为1或2的数组手工代入。比如a[2,5], x5第一次mid0a[0]25所以如果填lmid1l变成1第二次mid1找到如果误填rmid-1r变成-1循环直接结束返回找不到。这种“特殊小样例代入法”能解决90%的完善程序检查。还有一类完善程序涉及动态规划例如最长上升子序列或背包空位多半在状态转移dp[j] max(dp[j], dp[j - w[i]] v[i])的两个下标上。这时要检查一维滚动数组的遍历方向0-1背包需要倒序遍历因为要保证每个物品只使用一次完全背包可以正序遍历。掌握这个方向判别在初赛中就是一个重要的得分点。说白了完善程序补的不是句子而是“算法决策的边界”。6. 从真题到考场30天备考规划与应试细节6.1 三轮复习法铺基础、啃真题、磨错题如果已经有一定基础建议留出30天做三轮复习。前两周第一轮按专题过基础知识每天一个板块比如周一到周三过进制、编码、存储单位周四周五过操作系统和网络周末过C语法与数据结构。每过一个板块都要做配套的10道左右小题不要只“看明白”要动手算。第二轮选择近5年至少3套第一轮真题严格模拟考试一次性做完记录每部分实际用时和正确率。第三轮用错题倒逼薄弱点把错题按错误分型归类计算粗心型、概念混淆型、读题遗漏型、追踪断线型。这一步做扎实了你会发现最后一周根本不用再刷整套题只需要看错题本。模拟练习时一定用纸质试卷或关闭自动判分的文档自己判分并把分数记下来。很多选手习惯在电脑上做题第一轮却是笔试填涂答题卡、在草稿纸上演算、用铅笔在试卷上画变量表这些手感都要提前适应。否则考场上会发现自己“能想明白但没地方算”或者因为草稿太乱找不到上一个关键步骤。6.2 考场时间分配与检查顺序细节决定“压线晋级”还是“遗憾落选”关于时间分配我习惯把整场考试切成几个时间盒单选题放在最前面但考点分散遇到卡顿不要恋战先把有把握的做掉总用时控制在合理范围内阅读程序题留足逐段推演的时间完善程序题最后处理。当然具体题量各省可能有微小差异原则是“先易后难、先密后疏”把最可能拿分的板块先保住。考场上最怕的不是不会而是在一道难题上消耗过多导致后面连续丢分。检查时不要从头到尾重读一遍题目而是按“填涂是否完整—标记题是否还有思路—程序输出题是否重新推导一遍—计算量较大的题是否用特殊值验证”的顺序进行。单选题如果有拿不准的用排除法去掉最荒谬的项再用代入法把备选项带回题目描述试一下。判断题看到“一定”“必须”“所有”这类绝对化表述时要格外警惕因为命题人经常在这里设置反例。我在实际辅导过程中发现初赛翻车的孩子绝大多数不是知识面不够而是到了考场没有一套稳定的流程要么在选择题上耗到阅读程序没时间要么做完不检查就交卷。最后分享两个很土但有效的习惯平时练习就把草稿纸编号每道题的计算都写在固定区域方便复盘考前一天不要刷难题只翻错题本和复杂度表让大脑在赛前保持“熟悉感”而不是“紧绷感”。这一篇覆盖面不小但真正的消化方式还是落到真题上祝大家第一轮顺利。
