东华OJ机试题22-70刷题指南:从多组输入到边界条件的避坑攻略

东华OJ机试题22-70刷题指南:从多组输入到边界条件的避坑攻略 2023年东华大学OJ机试题22-70这批题我是从带学弟学妹准备复试机试时开始反复接触的。东华OJ上的题号区间跨度大但22到70这一段恰好卡在“入门刚过、算法未深”的过渡地带很多人在这个区间刷题时会出现一种典型状态——题目看得懂代码写得出一提交就是WA或TLE卡到怀疑人生。这篇文章不打算逐题贴AC代码而是把这批题背后真正值得沉淀的解题思路、输入输出处理、边界条件意识整理出来。不管你是正在备战考研复试还是单纯想用OJ题单巩固基础这篇都值得看完再动手刷。1. 题号区间背后22-70这批题究竟在考什么1.1 22-70这段题号的“能力分层”逻辑先解释一个很多人没意识到的问题东华OJ的题号顺序不完全是按难度递增排的。22到70这个区间我刷下来最大的感受是——它在刻意混合“纯语法题”和“需要一点点算法思维的题”。前段题号22-40左右大量考察C/C或Java的基础语法比如多组输入输出、数组操作、简单循环、字符处理这类题目在OJ上的通过率看着不低但实际考试时最容易翻车。为什么因为考生平时写代码用的是IDE输入数据是自己敲的而OJ评测是黑盒输入格式差一个空格、输出多一个换行就是0分。中段题号40-55左右开始出现需要“绕一下”的题目了。我印象里这批题里有不少是模拟题——题目描述特别长背景故事特别多剥掉外壳以后核心逻辑可能就十几行。这类题考察的是信息提取能力你能不能从一段冗长的描述里抓住状态变化的关键规则。很多人栽在这里不是因为不会写代码而是因为读题读到一半就开始写写完才发现少考虑了一个条件。后段题号55-70进入了基础算法层。排序、二分、简单动态规划、字符串匹配这些经典考点开始密集出现。这里的难度不是算法本身有多高深而是你知道这个知识点和你能在30分钟内把它写对是两码事。举个最典型的例子你能口述快排的思路但让你在白板上写一个不会越界、不会死循环的快排实现很多人写三遍都不一定对。1.2 我为什么推荐拿它当复试前的过渡训练我自己带过的学生里有基础很好的也有真正零基础的。对于基础好的那批直接刷洛谷或者力扣的hard题其实意义不大因为复试机试的难度通常卡在“中等题偏下”这个档位你花大量时间啃难题考场上遇到的反而是基础题。对于基础一般的同学一上来就刷难题容易挫伤信心刷太简单的题又起不到训练效果。22-70这个区间刚好卡在中间。我有个习惯让准备复试的同学先把这几十道题按“自己实现一遍→对照更优解法→横向总结题型”的方式过一遍。你会发现这个区间里涉及的技巧翻来覆去就那么几个——多组输入处理、排序、模拟状态转换、字符串处理、简单的动规递推。把这些技巧练到“肌肉记忆”的程度复试机试的底子就稳了一大半。要知道东华复试机试的题量和时间比例决定了你几乎没有时间在考场上现想算法大多数题拼的是熟练度和稳定性。2. 输入输出处理OJ机试里最容易被忽视的隐形扣分点2.1 多组输入到底怎么读才算稳22-70这批题里多组输入的出现频率非常高。这也是OJ题和平时写业务代码最大的区别之一——你不知道评测数据里有几组输入程序得自己判断何时结束。很多第一次接触OJ的人会写这样的代码以C为例int n; cin n; while (n--) { // 处理逻辑 }这种写法不是不行但它隐含了一个假设输入里第一行就是测试组数。而OJ题里另一种更常见的格式是“输入包含多组测试数据每组占一行读到文件末尾结束”也就是EOF结束。正确的写法应该是int a, b; while (cin a b) { cout a b endl; }用Java的话对应的是Scanner sc new Scanner(System.in); while (sc.hasNextInt()) { int a sc.nextInt(); int b sc.nextInt(); System.out.println(a b); }这里有个细节很多人不知道cin a这个表达式本身是有返回值的当读到EOF时它会返回一个“假值”循环自然终止。而Java的hasNextInt()也是一样的逻辑。理解了这一点你就不会在多组输入上纠结“到底要不要加一个终止条件”——评测数据根本没有“终止条件”这一说文件读完了就结束了。但在实际辅导过程中我发现了一个更隐蔽的坑混用cin和getline。cin读取时不会消费换行符如果你在cin n之后马上用getline()读字符串读到的会是一个空行。这个问题在OJ题里特别常见因为很多题是先给一个整数再给若干行字符串。解法很简单——在cin之后额外调用一次getline()把残留的换行符吃掉或者统一用getline()读整行再手动解析。2.2 格式化输出空格、换行、小数位的三个经典坑先问一个问题OJ判题系统是怎么判断你的输出“对不对”的答案是基于文本的逐字符比对。多一个空格、少一个换行、小数位数不对全部判错。这听起来很苛刻但这就是OJ的规则。22-70区间里就有好几道题专门在这种地方埋坑。第一个坑是行末空格。有些题目要求输出“每个数之间用一个空格分隔”很多人会这样写for (int i 0; i n; i) { cout a[i] ; }这样输出的结果是最后一个数后面也带了一个空格。如果题目要求严格这就是一个PEPresentation Error格式错误。更稳妥的写法是for (int i 0; i n; i) { if (i 0) cout ; cout a[i]; }第二个坑是浮点数精度。题目要求“保留两位小数”时C用printf(%.2f, x)或cout fixed setprecision(2) x都没问题。但要注意fixed和setprecision一旦设置后续所有浮点输出都会受影响如果你在同一个程序里有时保留两位、有时保留三位记得用完要恢复默认状态。第三个坑最容易忽略——输出数据之间的空行要求。有些题会说“每组输出之间用一个空行隔开”这意味着最后一组输出结束后不能有多余空行。我当年刷题时经常在这里连续WA几发后来养成一个习惯把“是否输出过数据”作为一个标记变量只有第二组及以后的数据才在前面额外输出一个换行。这个小技巧在东华OJ这类注重输出格式的题目里极其好用。3. 高频考点的“退役选手模板”直接套用的解题骨架3.1 模拟题先想清楚状态再动手模拟题是22-70区间里占比很大的一类。所谓模拟就是题目描述了一个过程或规则你按规则一步步走一遍把结果算出来。但模拟题有个特点规则一多代码就容易写得又臭又长最后自己都不知道哪里出了bug。我见过太多人一上来就写写到一半发现漏了一个条件然后疯狂打补丁最终代码乱成一锅粥。我的建议是模拟题先画状态转移图别画那种严格的流程图就画几行草稿把“在什么状态下遇到什么输入变成什么新状态”理清楚。举个区间里常见的方向给定一个初始坐标和一系列移动指令求最终坐标。这种题的骨架非常固定当前状态(x, y) 读取指令 如果是UPy 1 如果是DOWNy - 1 如果是LEFTx - 1 如果是RIGHTx 1代码写出来可能就十几行但如果你没想清楚就开始写很容易在“先更新x还是先更新y”“坐标原点在哪”这种细节上出错。另一个应对模拟题的技巧是把题目的规则用注释写出来一行规则一行代码。一方面方便自己排查逻辑另一方面万一出了问题对照注释定位也快得多。3.2 排序与查找不要只会调API排序是22-70区间里绕不开的话题。C里有sort()Java里有Arrays.sort()Python里有sorted()。很多同学觉得“排序题有什么好练的一个API不就搞定了”但OJ不会让你这么好过。先说一个最常见的坑自定义排序规则。题目可能会给你一个结构体/类比如学生信息包含学号和成绩要求“按成绩从高到低排序成绩相同则按学号从低到高”。这时候如果只调默认排序答案必错。你需要传入一个比较器。C里可以这样写struct Student { int id; int score; }; sort(stu, stu n, [](Student a, Student b) { if (a.score ! b.score) return a.score b.score; return a.id b.id; });Java的写法类似就是实现一个ComparatorStudent。关键点是多关键字排序时比较器的写法一定要逐层分清楚。再说查找。二分查找是复试机试的高频考点但很少有人能一次写对。这里分享一个我在这个题单区间反复验证过的模板// 在升序数组a中查找第一个target的位置 int l 0, r n; // r取n表示闭区间[l, r) while (l r) { int mid (l r) / 2; if (a[mid] target) r mid; else l mid 1; } // 循环结束后l就是答案如果ln说明target大于所有元素这个模板的关键在于区间的定义[l, r)左闭右开。你只要记住这个区间定义所有的边界问题都不容易出错。为什么因为r mid和l mid 1这两种写法保证了区间不断缩小且永远不会死循环。相比之下闭区间写法[l, r]很容易在mid取到l时出现死循环。3.3 字符串与数学机试里的“送分题”陷阱字符串处理在这个区间里也是重头戏。常见的操作无非是反转、统计、查找子串、判断回文、大小写转换。这里最大的陷阱不是不会做而是你用的API在不同编程语言里行为不一致。举一个我经常举的例子C里str.substr(pos, len)的第二个参数是长度而Java里substring(beginIndex, endIndex)的第二个参数是结束位置不含。如果你平时用Java写业务代码习惯了机试时换成C非常容易在这里翻车。我建议备考阶段只专注一门语言把一门语言的字符串API彻底吃透不要东一榔头西一棒槌。数学题方面22-70区间里经常出现的是最大公约数GCD、最小公倍数LCM、素数判断、质因数分解。这些都是有标准模板的但很多人喜欢临时推。GCD的模板辗转相除法必须烂熟于心int gcd(int a, int b) { return b 0 ? a : gcd(b, a % b); }最小公倍数就是a / gcd(a, b) * b注意先除后乘避免中间结果溢出。素数判断有一个常见的优化只需要判断到√n就行而且可以跳过偶数。bool isPrime(int n) { if (n 2) return false; if (n % 2 0) return n 2; for (int i 3; i * i n; i 2) { if (n % i 0) return false; } return true; }这里要注意i * i n在i很大的时候可能有溢出风险虽然在这个题单的范围内不太会出现写成i n / i更稳。4. 边界条件和数据范围失分最隐蔽的三个角落4.1 int溢出和取模运算的写法22-70这个区间里有的题目会设置一些“看起来不大”的数值范围实际上中间计算结果会炸掉int。最典型的例子就是斐波那契数列。斐波那契数列到第46项的时候就已经超过int的上限2147483647了如果你用int存算到第50项结果全是错的。很多人在处理这种题时会想“反正题目说答案对1000000007取模那我先把完整结果算出来再取模不就行了”——不行如果你在运算过程中已经溢出了取模也救不回来。正确的做法是在每一步运算中都取模const int MOD 1000000007; f[0] 0; f[1] 1; for (int i 2; i n; i) { f[i] (f[i - 1] f[i - 2]) % MOD; }还有一个常见场景是求组合数或者阶乘。n!在n14的时候就已经超过int上限了实际上13!是6227020800已经超了有些题会要求“结果对1000000007取模”而你用long long计算每一步并取模才能保证正确。我这边的建议很简单看到数据范围第一反应先判断它的答案或中间结果能不能塞进int。如果拿不准直接全体用long long。在东华OJ的评测环境中用long long的空间开销完全可以忽略不计没必要为了省几个字节去赌它不会溢出。这是一个用无数WA换来的教训。4.2 数组下标的“差一错误”与越界差一错误off-by-one是OJ刷题里最隐蔽、也最让人抓狂的错误。程序不会崩溃但答案就是不对。常见的两个场景第一个是前缀和。很多人喜欢写sum[i] sum[i - 1] a[i]然后访问的时候用sum[r] - sum[l - 1]下标非常容易搞混。我的建议是要么全部从1开始计数要么全部从0开始不要混用。如果题目中的数组元素是从0开始存储的前缀和数组通常开成n1大小sum[i]表示前i个元素的和即sum[0] 0sum[i] sum[i-1] a[i-1]。这样统一之后查询区间[l, r]的和就是sum[r1] - sum[l]。第二个是循环边界的写法。for (int i 0; i n; i)和for (int i 0; i n - 1; i)是等价的但实际调试时 n的写法不容易出错。反而 n的写法如果配合数组下标使用很容易造成越界访问——因为数组的有效下标是0到n-1a[n]已经越界了。我记得帮一个学弟排查一道题时他的代码逻辑看起来完全正确但偶尔会WA。最后我一行一行看过去发现他在循环里访问了a[i1]当i等于n-1的时候越界了读到了一个随机值。这种错误在OJ上是最恶心的因为本地运行可能碰巧没崩但评测机器上就跑出了奇怪的结果。4.3 空输入、空串、负数的特判还有一个容易被忽略的隐蔽角落是“极端输入的物理意义”。比如题目说“输入一个整数n0 ≤ n ≤ 100”很多人直接开始写逻辑但n 0的时候要输出什么是一行空行还是什么都不输出还是输出一个特定标志这个问题在普通编程里无所谓但在OJ上就是生与死的区别。字符串题也一样。如果题目说“输入一个可能为空的串”你的代码要能正确处理空串的情况。用getline读到一个空行时字符串是如果你直接访问s[0]在C里这是未定义行为程序可能直接崩溃RE。负数的处理是另一个高频踩坑点。比如计算绝对值的题直接用abs()函数时对于int最小值-2147483648它的绝对值还是-2147483648因为int能表示的最大正值是2147483647绝对值溢出了。这种极端值如果没特判就会得到错误答案。我自己的处理习惯是写代码之前先列几个特殊输入——最小值、最大值、0、空串、负数、单个元素。答题时把这些特判写在最前面再开始写主体逻辑。这个习惯让我在OJ刷题上的WA率下降了一大截。5. 一次真实翻车记录我卡在一道“简单模拟题”上的完整排查过程理论上这一节应该分享一道22-70之间的具体题解但我不打算直接贴题号因为OJ题库可能调整而且每次调整后题号和题目对应关系都会变。更值得分享的是我一次非常典型的排查经历这类经历几乎每个人都会遇到。那次我帮一个学弟调一道坐标模拟题。题目描述大概是一个机器人在坐标平面上移动接收一串指令每个指令由方向和步数组成最后输出终点的坐标。学弟把代码发给我逻辑看起来完全正确读取指令次数n循环n次每次读取一个字符和一个整数根据字符判断方向更新x或y。但提交就是WA。我让他把测试样例在本地跑一遍全对。这就很奇怪了——样例能过但评测过不了典型的“边界条件没处理好”。我开始逐行排查第一步检查读入方式。题目里指令和方向之间是用空格分隔的但评测数据的行尾可能有不可见的\rWindows换行符。如果读取时没处理掉字符变量可能会读到\r导致判断方向时进入不了任何分支。解法是在读取方向字符前用空格匹配的方式跳过空白字符。这是我排查的第一个重点——OJ评测系统里的换行符并不总是你自己想象的\n。第二步检查坐标范围。题目说坐标步数在[-10000, 10000]学弟用了int这个范围没问题。但有一类题会在移动过程中“越界”比如坐标有上下界每次移动前要判断是否越界如果越界就停在边界。学弟完全没有处理这个逻辑。很多模拟题都把这种隐藏规则写在题目描述的不起眼角落里扫一眼就过去了。第三步检查输出格式。题目要求输出“x y”中间用一个空格分隔末尾要不要换行其实大多数OJ都无所谓因为忽略行末空白但他在输出里多了一个制表符\t——题目说的是空格他用了制表符这在OJ的字符比对里会判错。最后问题是出在哪一步是第一步。因为评测数据里确实存在Windows换行符\r\n学弟本地用的是Mac或Linux读到的都是\n所以本地测试永远正确。把字符读取方式改成忽略空白符之后整个程序就过了。这次排查给我的印象非常深。它说明了一个道理OJ题里90%的“玄学报错”都不是玄学而是输入数据和你本地环境的差异造成的。以后再碰到“本地对OJ不对”的情况我的排查顺序永远是读入方式 → 数据范围 → 边界条件 → 输出格式。6. 接下来怎么练给不同基础的同学一份可执行的路线图6.1 从22题刷到70题的最小闭环如果你是零基础或者基础比较薄弱的同学我不建议按题号顺序从头刷到尾。题号顺序不完全等于难度顺序按顺序刷容易在某一类题上反复碰壁既浪费时间又打击信心。我给你一个“按考点分组”的最小闭环策略第一轮只做“输入输出”和“基础语法”考点的题。目标是所有题都能一遍提交通过不允许有编译错误和格式错误。这一轮练的是手感和规范。第二轮做“模拟”和“字符串”考点的题。这一轮的要求是不管题面多长、规则多绕都要能心平气和地拆解成状态变化然后写出来。计时45分钟一道超时就跳过回头再看题解。第三轮做“排序”“查找”“简单DP”考点的题。这一轮的题目往往需要一点算法技巧重点是总结模板。每做完一道题写一个笔记把这道题用的核心模板抽出来形成自己的代码片段库。三轮全部过完之后再回到题号顺序把之前跳过的题补一遍。这时候你会发现很多之前觉得特别难的题现在看就是“老朋友”几分钟就能AC。6.2 复盘方式比刷题量重要我带过太多同学刷题一上来就追求“一天刷20道”但刷完就忘等于白刷。这里分享一个我用了很久的复盘模板每道题都要回答这几个问题这道题考的是哪个知识点写出一两个关键词我的第一反应解法是什么和最优解法的差距在哪里我在哪一步卡住了是因为没读清题目还是因为某个API不会用有没有可以背下来的通用模板比如二分模板、GCD模板、多组输入模板如果我下次再遇到类似的题第一步应该做什么这些问题回答完才是真正把一道题的价值榨干了。我特别想强调最后一条“下次再遇到类似的题第一步应该做什么”。这一条决定了你的知识能不能迁移。举个例子你刷了一道“将十进制转为二进制”的题下次遇到“将十进制转为十六进制”你要做的不是重新读一遍题而是直接意识到“这是同一类题用同一个模板”。这种题目归类能力就是通过复盘训练出来的。6.3 考前一周的“考场模拟”清单最后说一下考前一周怎么用22-70这批题来模拟考试。很多同学直到上考场都没有完整地模拟过一次“机试环境”这是一个很大的失误。复试机试有严格的时间限制东华往年一般就是1到2小时环境中也没有IDE的自动补全和智能提示甚至有时候连编译错误的信息都要你自己猜。所以考前至少做2次完整的模拟。模拟方法很简单选一个安静的时段定好计时器随机挑5到8道题要覆盖不同考点不开任何IDE的代码提示功能强迫自己在一个裸编辑器里写代码。遇到不会的题不要马上看题解先跳过做后面的最后再回来思考。模拟结束后把自己卡住的题整理出来重点复盘——这些题就是你真正需要再复习的薄弱环节。我在模拟中发现过一个很常见的现象很多同学平时刷题时间充裕一道题可以花两三个小时慢慢磨但机试是有时间压力的一旦心理紧张平时会写的东西也可能写不出来。所以模拟的目的不是复习知识点而是适应那种“必须在规定时间内产出可运行代码”的心理状态这一点太重要了。还有一个细节机试时提交前一定要再检查一遍输出格式。我见过不止一个人样例都能过但输出里多了一个空格或者少了一个endl整个题直接零分。这种失误不是能力问题完全是习惯问题——提交前花10秒钟检查一下输出语句是性价比最高的得分手段。从22题刷到70题说多不多说少不少。真正能把这一区间全部吃透的人基础一定差不了。关键不在刷了多少道而在于每一次提交、每一次报错、每一次翻看题解之后你从里面带走了什么。OJ刷题这条路没有捷径但踩坑的经验是可以传承的。希望这篇东西能帮你少走一点弯路把时间和精力花在真正该花的地方。