作为刚入学就一头扎进新生赛的过来人,我打算把这段参赛经历完完整整记下来。标题写了【自述,更新中】,一是因为这文章本来就是一版一版改出来的,二来比赛前后心态和认知变化太大,回头看上一次写的东西,经常觉得自己当时太天真。新生赛在很多学校就是一场给新生的算法竞赛,也叫校赛、热身赛,内容从排排坐写A+B,到需要一点脑筋的规律题,再到看着就像是给大佬准备的压轴题,什么层次都有。这篇自述,我不打算写成一篇标准的攻略,而是把我从报名、备赛、比赛当天到赛后复盘的全过程,包括踩过的坑、想不明白的题、心态崩了又捡起来的瞬间,都摊开来讲。对零基础想打比赛的人,这篇内容应该能让你少走很多弯路;对已经准备参赛的,你可以直接抄我复盘时总结的那套方法和避坑清单。
1. 新生赛到底比什么
1.1 赛事定位:一场给新人的算法体验课
很多人一听“新生赛”就紧张,以为这是要跟各路大佬拼个你死我活。实际参加过就明白,新生赛最核心的定位不是选拔,而是体验。学长学姐办这场比赛,很大程度上是想告诉刚进校的同学:编程不是只在课本上背语法,它还可以是一场限时解题的游戏,而且这场游戏有明确的规则、有即时反馈、有排名压力,玩起来非常上瘾。
以我们学校的情况来说,新生赛通常安排在开学后第六到第八周,报名人数往往超过三百人,其中很大一部分在报名时连map和set都没用过,甚至有人只写过程序设计的课后作业。比赛语言以C/C++为主,也有少数用Java或Python,但数量很少。赛制四小时,十道题,按通过题数和罚时排名。这里说的罚时,就是每道题从比赛开始到第一次正确提交之间经过的时间,加上错误提交额外增加的惩罚时间(通常是20分钟)。也就是说,你不仅要做得对,还要做得快,而且不能乱交、反复交。
那它到底在考什么?我个人的体会是,新生赛考的更多是“在有限时间内,用你现有的知识解决问题”的能力,而知识本身的门槛其实非常低。大部分题都是基础模拟、简单枚举、二分查找、前缀和、贪心、简单DP这一类。说白了,真正的难点并不在算法有多高深,而在你能不能读懂题、能不能把思路转化成代码、能不能在debug时保持冷静。这三件事,恰好是大学四年乃至以后工作里最核心的编程素养。
1.2 题目结构:从签到题到压轴题的难度梯度
新生赛的十道题,难度分布是有套路的。正常情况是:
| 题目类型 | 数量 | 难度 | 典型考点 |
|---|---|---|---|
| 签到题 | 2-3道 | 极低 | 输入输出、简单运算、字符串处理 |
| 基础题 | 3-4道 | 中等 | 排序、模拟、二分、贪心 |
| 进阶题 | 2-3道 | 较高 | 前缀和、DP入门、搜索、并查集 |
| 压轴题 | 1-2道 | 很高 | 数学构造、复杂图论、状态压缩 |
“签到题”这个名字很形象,就是让你来了、坐下、签到,拿一血,建立信心的题目。比如第一题经常是“给你两个数,输出它们的和”,或者“给你一串字符串,倒着输出”。这种题考察的不是智商,而是你的环境有没有配好、提交流程走没走通。如果你连签到题都WA了好几次,大概率不是脑子的问题,而是读入输出格式、数组越界这类低级问题。
中段的题开始出现区分度,常见套路是模拟题和二分题。模拟题会给你一段文字描述的场景,比如模拟一个排队过程、一个日历系统、一个打分规则,你得把过程用代码走一遍。这类题最坑的是边界条件多,容易漏情况。二分题则偏思维,重点不在代码,而在“能不能想到答案具有单调性,可以用二分去逼近”。
进阶题已经接近正式的算法题了,像前缀和用来快速求区间和,简单DP用来求最短路径数、背包问题之类,DFS/BFS则是搜索题的基石。到压轴题,往往就不是“会不会算法”的问题,而是“能不能数感爆棚发现规律”的问题,很多数学构造题代码只有三五行,但思路就是想不出来。
我在备赛时把这些层次摸清楚以后,才明白一个道理:新生赛的成绩,并不取决于你临时学了多少高深算法,而取决于你能否把基础题稳稳拿满、进阶题尽量抢分。目标定得太高,容易摔得很惨。
2. 报名与备赛:我踩过的第一个坑是人多
2.1 报名时最容易忽略的几件事
新生赛报名看起来简单——填个表、加个群、完事儿。但这里面的坑比想象中多。
第一个坑是“我以为报名了就有比赛场地”。我们学校赛前三天才在群里发公告,说今年因为机房设备问题,线上赛改到指定教室,而且是每三十人一个大教室,间隔坐人。结果很多同学没看群消息,当天跑错了地方,折腾半个多小时。所以报名以后第一件事,不是买书,不是刷题,而是把比赛群置顶,把群公告从头到尾读三遍,甚至主动去问学长学姐“赛前还会不会变”。别嫌麻烦,一次变动就可能让你开赛前心态乱掉。
第二个坑是环境准备。有些同学平时用Visual Studio写代码写得挺爽,但OJ(在线判题系统)不吃这一套。比赛用的是Linux环境下的GCC编译,跟Windows下的VS有一些差异,比如scanf/printf和cin/cout混用的坑、long long在Windows和Linux下的大小差异、头文件要不要写cstdio这些细枝末节。建议提前两周就用OJ的在线IDE或者本地装一个MinGW环境,照着比赛的编译选项配置好。我在备赛时就吃过一次亏:本地用Dev-C++跑得好好的代码,一交上去直接编译错误,原因是没有包含<map>头文件,本地编译器自动帮我补了依赖,OJ不会。
第三个坑是队友或单人问题。新生赛有些学校是单人赛,有些是三人组队赛。大家报名时会默认找室友、找同班同学凑队,但很少考虑“性格适配”。组队赛最忌讳两件事:一个是不说话,各做各的;另一个是抢键盘,谁都不服谁。我见过一个宿舍三兄弟组队,比赛前说好怎么分工,结果一开赛全都扑向最简单的签到题,抢了五分钟键盘,最后三个人挤在一个代码里改了又改。后来我们队定了清晰的规矩:签到阶段一人写代码,其他人读后面的题;遇到卡题,每人单独思考十分钟,再集中讨论。这个规范看着简单,保住了我们后两个小时的心态。
2.2 零基础到新手需要的三块敲门砖
如果你现在刚学完C语言的循环和数组,离能去新生赛打一场至少还差三块敲门砖。
第一块是STL。C++的STL就像编程世界的工具箱,vector是能自动扩容的数组,map是键值对字典,set是自动去重和排序的集合,sort是随手可用的排序函数,queue和stack分别是队列和栈。没有STL,你用数组手写这些结构的代码量是STL版本的三到五倍,还极其容易写错下标。新生赛里大量题目不需要你实现结构本身,只需要你会“调用”它们。所以零基础选手优先学STL,性价比极高。
第二块是读题能力。这句话听起来像废话,但我在赛后复盘时发现,十道题里我有两道是“读题不仔细”翻车的。一道题要求输出结果保留两位小数,我看漏了,直接用整数输出,WA了三次;还有一道题说数据保证没有重复,我却写了一大段去重逻辑,白白浪费时间。新生赛的题面普遍喜欢把关键条件藏在中间段落甚至最后一句,你跳着读就中招。后来我给自己定了个规矩:任何题目必须读三遍,第一遍通读,第二遍把输入输出格式、数据范围、特殊条件画出来,第三遍在草稿纸上用自己的话复述一遍题目,确认理解无误再动手。
第三块是debug能力。新手写代码最大的问题不是不会写,而是出了问题不会查。最常见的报错类型有编译错误(少括号、缺分号、类型写错)、运行错误(数组越界、除零)、答案错误(逻辑不对、边界漏判)。我自己的Debug流程是:先从报错信息定位问题所在行;再看数据范围,检查有没有超过int的变量,能用long long就尽量用;然后构造几组极端数据,比如空数组、只有一个元素、最大值、最小值、重复值,一步步缩小出错范围。掌握这套流程以后,WA从“抓狂半小时”变成了“五分钟内定位”,节省下来的时间比多学一个算法更有价值。
2.3 我的备赛节奏:三周高强度加一周稳住状态
我的备赛周期满打满算四周,前两周半是“广撒网”,后一周半是“稳状态”。
第一个阶段,跟网上的公开课程和刷题网站走。我选的是洛谷和Codeforces。洛谷的题库有题目难度标签,特别适合新手按图索骥;Codeforces则主要是用来模拟比赛节奏,它的题目区分度很好,哪怕只做出A题也能锻炼赛场心态。每天我保证两到三题的刷题量,不是数量上的刷,而是每道题都写成完整代码并在OJ上通过。这里说个经验之谈:只读题不写代码,等于没学。很多题目你觉得“思路很简单”,一写代码就各种报错,这就是典型的眼高手低。
第二个阶段是专项突破。我在第一周扫了一遍自己容易出错的题型,发现自己排序、二分、简单贪心都能做,但一到递归和搜索就发怵。于是花了两天时间集中看DFS和BFS,用模板题反复练,直到能默写核心框架。参加新生赛不需要面面俱到,但要确保基础题型里没有明显的短板,因为比赛是木桶效应,一道题卡住四十分钟,比你放弃它去做别的更影响心态。
第三个阶段是模拟赛。比赛前十天,我做了一次完整的线上模拟,直接在Codeforces找一场难度合适的老人家新手赛,按正式赛的四个小时来计时。第一次模拟我只做出两道题,而且第二题提交了七次才过,罚时惨不忍睹。第二次模拟已经能稳定做出四道,并且学会了在卡题二十分钟后跳题。这两场模拟赛的价值远远大于我之前两周的零散刷题,因为它暴露出来的问题全是赛场上的致命伤:时间分配不合理、被罚时拖累、心态崩掉以后乱提交。
3. 比赛日现场实录:做题顺序与心态崩了怎么办
3.1 开场40分钟:先拿签到题,再定策略
比赛当天早上我提前四十分钟到了机房,从带的东西就能看出一个选手的成熟度:身份证、准考证、黑色水笔、三张草稿纸、一瓶水,还有提前打印好的STL常用函数速查表。很多人觉得比赛台上不能看资料,其实规则只禁止联网和讨论,自己的笔记完全可以带。我带速查表的目的不是为临时查,而是为了让自己心里有底:万一某个STL函数记不清参数,可以瞟一眼,省的因为这种小事浪费提交次数。
开赛前五分钟,我给自己定的策略只有一句话:前四十分钟只做前三道能确定拿下的签到题和基础题,把所有题目扫一遍。九点比赛正式开始,我先花两分钟把十道题全部看了一遍,在草稿纸上给每道题标注了预估难度和自己的信心级别。这个过程非常重要,因为有些题目看起来题目很长很吓人,数据范围却很小,直接暴力就能过;有些题目题干只有五行,却是整场比赛的最大陷阱。
开场十五分钟,我顺利提交了第一题和第二题,一血到手,排名一度冲进前十。这时候最容易飘,好在我提醒自己:前两道题是所有人的“保底题”,后面才是真正的比赛。第三道模拟题我开始认真对待,读了三遍题,确定了所有边界条件,花了大约二十分钟写代码和测试。提交时心里还有些不确定,幸好一次通过。三题到手,时间还剩三个小时,我给自己倒了一杯水,站起来稍微活动了一下,重新浏览剩下的题目。
3.2 中段推进:三小时里我试过的路线
第四题到第七题才是真正的分水岭,很多人在这个阶段开始出现“明明有思路,却写不对”的烦躁感,我也不例外。
第六题是一道二分题,X轴上有许多点,要在指定位置装信号塔,要求覆盖所有点的最小半径。这种题我在备赛时练过类似的,按理说稳拿。但问题出在我对精度的处理上:题目要求输出结果保留一位小数,我用了整数二分,结果最后一组数据死活差0.1。我在那题上卡了二十分钟,挣扎着想用浮点二分,又怕超时。这时候我做了整场比赛最正确的决定之一:把第六题先放一放,去看第七题。
第七题是一道前缀和的裸题,求一个数组里所有长度为K的子数组的最大和。看到数据范围是十万级别,我马上就想到滑动窗口或者前缀和,三分钟理清思路,十分钟写完代码并测试了几组数据,提交一次通过。这道题给我的信心极大反弹,第六题卡住的焦虑消了大半。我再杀回去用浮点二分重写,把epsilon设成1e-7,提交也通过了。两题到手,排名又往前挪了不少。
我的体会是:赛场上最忌讳的并不是“不会做”,而是“困在一道题上”。连续四十分钟做不出有效进展,就应该立刻换题。换题不是放弃,而是把赛场上最廉价的资源——时间,分配到更有可能产生收益的题目上。我的节奏大概是:每道题最多投入三十分钟,三十分钟内必须有实质推进(写出了完整代码、或者想清楚了关键算法),否则先跳,回来再说。
3.3 卡题时刻:放弃也是一种策略
第八题是我这场比赛里比较狼狈的一道,一道搜索题,要在迷宫里数连通块。按我备赛的水平,这题应该能做出来,但我当时刚写完第七题,思路还在二分上,切换到DFS的思维花了很长时间。更糟糕的是,我在写bool数组标记访问时,把行列坐标写反了,导致死循环,直接运行超时。
这时候离比赛结束还剩五十分钟,排名卡在中游。我深吸一口气,把第八题的代码全部推翻,重新在草稿纸上画了一遍迷宫图。我突然意识到,这不是死循环的问题,而是我的DFS剪枝条件从一开始就错了:题目里允许斜向移动,意味着我从一个格子出发有八个方向,而不是四个。改掉这个条件后,代码逻辑通了,提交后一次通过。虽然这道题花的时间比我预想的多,但最终做出来的成就感也最强。
第九题是一道组合计数的数学题,我看了一眼就判断自己大概率做不出来。按正式赛的惯例,比赛结束前二十分钟我会停止尝试新题,专心检查前面几题的提交情况。确认所有代码都没有低级错误后,我试着给第九题写了一个暴力搜索,看能不能在数据范围较小的情况下骗到部分分,可惜结果超时,只收到一个WA,但我并不后悔。比赛的分数是“你通过了多少题”,而不是“你尝试了多少题”,能拿到的分先拿到,拿不到的分别硬耗,这是新生赛给我上的第一课。
4. 因为一道题,我把整场比赛复盘了三遍
4.1 赛后第一件事:不是查排名,是看提交记录
比赛结束的那一刻,我做的第一件事不是打开实时排名,而是把网页切到自己的提交记录,一题一题地看每道题目的提交时间和结果。这个习惯来源于我备赛时养成的“错题档案”意识,只是当时记录在笔记本上,这次记录在脑子里。
我发现了几个问题:第一题我虽然一次通过,但在通过之前犯了一个非常低级的错误——读入时把%d写成了%s,编译直接报了warning,我在本地没注意,第一次提交直接编译错误。这种错误如果发生在比赛后段,很可能会让我心态崩掉。第二点,我的罚时其实不低,第七题虽然在规定时间做出来了,但我因为不放心,反复提交了好几次相同逻辑的代码,白白加了好几次罚时。这提醒我:提交前应该再花三十秒冷静检查,而不是抱着“碰碰运气”的心态。
我开始把整场比赛按时间轴重新在草稿纸上画出来,标出每道题投入的时间段、提交状态、出错原因。这个过程看起来琐碎,却让我对比赛的全貌有了把握:我的得分基本来自签到题和前段的基础题,中后段能力不足的问题被“题目顺序相对友好”掩盖了。如果换一套乱序题,我大概率会扑街。
4.2 复盘模板:每道题都要回答的四个问题
赛后复盘我没有停留在“哇那题好难”的情绪里,而是建了一个表格,把十道题都填了进去。表格的列分别是:题目编号、我的思路、正确的做法、差距在哪里、下次怎么办。
这个模板看起来简单,但非常有效。以第一题的编译错误为例,“我的思路”是不存在的,纯粹是手误;“正确做法”是读入和输出都用标准的scanf/printf,不要在类型上犯迷糊;“差距在哪里”是我没有形成提交前自检清单;于是“下次怎么办”明确记录:提交前检查三件事——头文件是否完整、变量类型是否匹配、数组大小是否越界可能。
第二道题是排序结构体,我WA了一次,原因是排序函数没写清楚,结构体的比较条件是“分数不同按分数排,分数相同按学号排”,我在cmp里只比较了分数。这道题的复盘让我养成了一个习惯:任何自定义排序函数写完,先拿两组包含边界条件的样例测试,而不是直接交。
每一道题的复盘都指向一个具体可执行的改进动作。等到十道题全部复盘完毕,我发现新生赛教会我的并不是多少算法知识,而是一套自我纠错的方法论。这套方法论在后面的课程设计、面试刷题甚至代码调试中一直有用。
4.3 新生赛教会我的三件小事
第一件事是:承认自己“暂时不会”并不丢人。复盘的表格里,第八、第九、第十题我都标着“未想出”,但正因为承认了不会,我才能在赛后几天有针对性地补搜索和组合数学。如果死要面子假装题目没意思,那这场赛就白打了。
第二件事是:比赛是学习的加速器。平时我学一个算法可能拖一个星期,但为了准备新生赛,我两天内学完了DFS和BFS,三天内刷了二十道二分题。比赛给你一个明确的时间节点,你的效率会指数级提升。不要等到觉得“自己准备好了”再参赛,因为人类永远不可能准备好,比赛的意义就在于“在限定时间里用现有水平全力以赴”。
第三件事是:排名不是最重要的,路线才是。我见过比赛成绩中等、但赛后一周内把复盘里所有“不会”的题都AC掉的同学,他的成长速度远远快于那些排名靠前、但打完就丢下比赛的人。所以我会劝下一届的新生:赛前好好准备,赛中尽力发挥,赛后勤快复盘,这条流程走完,输赢都已经不重要了。
5. 给下一届新生的建议:现在就可以开始做的事
5.1 开学第一周就做这几件事
如果你明年想参加新生赛,不用等到报名开始才行动,开学第一周就能做很多事情。
第一,装好环境。在自己电脑上搭好一个能写C++、能编译运行的环境,不需要装太多花哨的东西,一个编译器加一个顺手编辑器就够。我推荐新手从Dev-C++开始,因为它开箱即用,装完就能写代码,等练习量上来再换VS Code或CLion都行。关键是别在环境上浪费太多时间,环境只是工具,核心是刷题量。
第二,找个刷题平台注册账号。洛谷、牛客、Codeforces三选一即可,注册完先把“新手村”或“入门题单”刷完。那个过程可能会很挫败,因为你会发现自己连输入格式都读不懂,但没关系,这是所有人的必经阶段。我当时第一次在洛谷交题,因为输出多了个空格WA了五次,气得想砸键盘,但正是那五次WA教会了我认真对待输出格式。
第三,找到组织。去贴吧、QQ群、社团招新或者直接问学长学姐,把你们学校ACM集训队或者编程社团的联系方式加上。新生赛前,这些组织一般会组织培训,哪怕只是线上的直播讲题,也比你自己闭门造车效率高得多。更重要的是,你会认识一群同样在准备新生赛的朋友,大家可以一起刷题、互相吐槽WA的原因,不至于一个人扛不过前期挫折。
5.2 把比赛当考试,考试当比赛
这句话是我在备赛第二周悟出来的。很多人平时写代码很随意,变量名随便起、中间过程不打印、出了问题全靠猜,但到赛场上突然变得极度紧张,连代码都敲不利索。这种割裂感会让你平时的水平完全发挥不出来。
我练了三个“平时要求”:第一,所有刷题都计时。哪怕是简单题,也要记录自己从读题到提交花了多久,目标是把简单题压在十五分钟内拿下,才能给后面的硬骨头留出缓冲。第二,以AC为唯一标准,不以“感觉差不多”为标准。平时写完代码,要在OJ上提交并通过才算结束,不能只在自己电脑上运行几个样例就草草了事。第三,尝试在有点饿、有点困的状态下做题。这不是自虐,而是模拟比赛的实际状态——比赛那天你会紧张、焦虑,状态远不如平时清醒,提前适应一下,赛场上反而能保持冷静。
反过来,比赛前一周可以把以往的高考题、模拟题安排得像考试一样来做:提前到场地、只看纸质题单、不聊天不查资料、严格计时。我在这次新生赛前十天做的那两场模拟赛,就是把Codeforces当模拟卷,收获巨大。
5.3 更新中的“新生赛”心态:比赛只是起点
这篇自述写到这已经很长了,但我的新生赛故事还远没结束。赛后我把自己的复盘文档放到博客上,陆陆续续收到很多同届和学弟学妹的消息,有人问我某道题的解,有人分享他自己的备赛计划,还有人问“学长,我零基础现在开始学还来得及吗”。我的回答永远是:来得及,种一棵树最好的时间是十年前,其次是现在。
我现在已经不再为比赛当天的排名焦虑,反而开始享受赛后总结、被提问、帮别人答疑的过程。新生赛让我养成的“限时解题”的训练方式,我打算一直练下去,一直更新下去。以后等我打完更多的赛事,回头再看这篇文章,大概又会嫌自己幼稚。但如果这篇自述能让后来者少踩几个坑,在赛场上多一分从容,那这洋洋洒洒的几千字就没白写。
最后再分享一个小技巧:比赛结束后,把复盘文档存成一份“写给明年自己的信”,放在你明年的日历上。等到你再次翻开的时候,会发现自己过去一年走了多么远的路。这份自述也在更新中,下一次更新,就是明年的新生赛现场。