
第一次用东华oj刷题的时候我站在原地愣了好几分钟。不是题目有多难而是每道题看起来都会做但提交上去就是红一片——Wrong Answer、Compile Error、甚至还有一次故意把用户界面当成聊天软件去发消息。回想起来有点好笑但这恰恰是每一个OJ新手绕不开的门槛代码写得对不对不是自己说了算是判题机说了算。这篇东西不是我随手翻别人的题解抄出来的而是我带着“入门阶段判题逻辑不清晰”这个问题从头到尾把东华oj前5道题重新过了一遍之后攒下的经验。如果你刚开始用东华OJ或者刷了好几次都在前几道题上反复吃瘪那这篇文章特别适合你。我会把平台的判题机制、这5道题背后锚定的基础考点、参考代码以及我在提交过程中真实踩过的坑全部拆开说清楚。1. 先把东华OJ的判题逻辑搞明白再谈做题很多人刷题的心态是“看到题就写写完就交”然后格外焦虑地盯着结果。实际上OJ系统Online Judge在线评测系统的本质是一个“只看输入输出是否匹配”的机器裁判。你提交一份源代码它把你的程序编译起来用一组测试数据跑一遍再把你的输出和答案逐一比较。比较通过就是ACAccepted有一点点不一致就是WAWrong Answer程序运行崩了就是RERuntime Error用了超过题目允许的时间就是TLETime Limit Exceeded。这里有个新人最容易误解的地方OJ不是让你把题目做对就算赢它要求你的程序在_多组、极端、隐藏的数据_上依然稳定通过。所以你在本地IDE里跑通样例只是拿到了及格线的一半。东华OJ作为大学里常用的练习平台测试点设计得不算刁钻但它确实会按“边界数据”来搞心态。比如题目说“输入两个整数”就会有人写死只读一次结果第二组测试数据一进来程序连个循环都不写直接输出一个数完事——这种代码样例能过但提交必WA。另外一个必须适应的点是东华OJ的反馈细致度。它不会告诉你哪个测试点没过只告诉你一个最终状态。所以刷题的过程实际上是一个“从黑盒结果反推问题”的过程。刚开始你可能很崩溃觉得这机器不讲道理但我建议你换个心态把OJ当成一个性格严谨的mentor它不会给你放水不会告诉你哪里错了。你要做的是get到它的思考方式。我整理了一下新手最常碰到的几个判题状态对照关系放在这里刷题的时候心里有谱状态缩写全称大致含义ACAccepted你的程序输出完全正确WAWrong Answer程序能跑通但输出和答案不一致CECompile Error代码没法通过编译语法/环境问题RERuntime Error程序运行过程中崩溃比如段错误TLETime Limit Exceeded程序超时运行得太慢PEPresentation Error格式错误多空格或少空格东华oj比较少见理解这套机制后你再看1-5题心态会稳很多。很多人觉得前5题无聊其实它们存在的意义不是考你会不会求两个数的和而是让你适应“程序结构必须同时满足多组数据、边界取值、精确输出”这三个基本条件。把这几题啃干净等于把OJ的脾气摸透了后面刷题会顺很多。2. 东华oj 1-5题的考点拆解与思路分析我刷的这5道题大致挨着的考点是这样的第一题基本输入输出第二题多组数据循环读取第三题格式控制输出第四题条件分支判断第五题简单循环求值。不同学期题单顺序或许有差异但前5题横向铺开想训练的能力就是“从只写顺序程序过渡到用分支和循环解决重复劳动”。2.1 第1题建立“标准输入输出”的第一感觉第一道题不管是“AB”还是“输出一句话”核心目标只有一个让你老老实实掌握scanf、printf或者cin、cout的格式。这道题没有任何算法含量但它是后面所有题的地基。很多新人在这里暴露出一个问题样例输入是“1 2”他们就在代码里写scanf(%d, a);来读一个数看到输出是3就把题目中的“空格”理解成“输出时也得有”结果到处都是多余的占位符号。这道题的正确打开方式是理解输入流的概念。scanf和cin本身是“流式地从缓冲区读取数据”它不关心你换行还是空格只按格式说明符去匹配。所以读两个整数就是连续两个占位符输出的时候严格按照题目要求的格式来。我在这个阶段给自己定了一个死规矩每一道题都先手工构造三组测试数据——样例数据、最小值边界、大一点的数值确保程序在这三种情况下输出都没问题再点提交。2.2 第2题学会“读完所有输入再停”从第二题开始大概率会碰到多组数据的题目。比如一直输入两个整数直到文件结束。这题的判断逻辑不难难就难在“怎么知道输入结束了”。OJ的测试数据都存放在文件里系统跑程序时会重定向标准输入。所以在C语言里你得用while(scanf(%d %d, a, b) ! EOF)这种写法在C里就是while(cin a b)。这个知识点特别重要因为很多人在本地测试的时候会手动敲数据敲完发现程序不退出就以为死循环了。实际上不是程序错了是本地没发结束输入。我自己就在这上面卡过IDE的调试窗口里输入完数据程序还挂着我以为代码写错了折腾了十分钟才发现要按CtrlZWindows下是CtrlZLinux下是CtrlD来模拟输入结束。搞清楚这个原理之后以后所有“多组输入”的题都畅通了。2.3 第3题输出格式精确到小数点后几位第三题多数学会牵扯到浮点数输出格式比如算均值、算华氏温度转换。这类题的隐藏考点是“当你输出的小数位数和题目要求不一致OJ会直接判WA”。你可能觉得多一位少一位无所谓但机器裁判不这么想。C语言里推荐用printf(%.2f, x)去控制精度C里用cout时需要配合#include 执行cout fixed setprecision(2) x。这里我要提一个特别鸡毛但真实存在的坑C的默认输出格式对于浮点数有时候会用科学计数法。如果你没有加fixed大一点的数字输出出来会变成“1.23e003”这种形态在OJ那里直接被判WA。所以能顺手加fixed就不要偷懒。另外四舍五入的问题。printf和C的默认setprecision都是四舍五入的但如果你手动写数学公式可能会因为浮点数精度舍弃导致最后一位偏掉一点。我的经验是输出阶段不要自己动手去改值直接用格式化控制别用round函数再去动一次因为有时候反而会因为double到float的截断引入误差。2.4 第4题从顺序走向分支学会“覆盖所有路径”前几题大概率还是线性跑的代码到第四题就进入if-else分支。考点通常类似于给一个成绩输出等级或者判断闰年。这题的目的是让你建立“完备性”思维——你的程序能不能应对所有合法输入甚至所有非法输入。我在写这类题时每次都会把if和else的对齐检查一遍。最容易犯的错是只在if里写了return或输出但else分支漏了或者是多个if并列而不是用else if导致多个分支同时命中的问题。比如说判断成绩等级写成if(score 90)输出Aif(score 80)输出B结果一个85分的学生会同时走进第二个if吗不会。但一个95分的学生会先走进第一个if输出A然后再走进第二个if也满足条件又输出一次B。这就是典型的多if非互斥带来的输出混乱。还有一个要注意的点边界值。题目说“大于等于90分为A”那90分到底算不算A当然算。但你要是写成if(score 90)那就少了一个边界。我刷到这种题会把所有边界数字单独列一遍像90、80、60这些值逐个代入验证一遍再提交。2.5 第5题循环的引入把重复劳动交给程序第5题开始出现for循环或while循环诸如“求1到n的和”、“打印一个序列”之类。它的意义很直接让程序能“自己重复动作”。很多新手在这里第一次碰到int类型溢出的问题。比如求1到n的和n的取值范围如果达到10万级别结果会是50亿以上已经超过了int的范围int通常最大21亿左右。这个时候你必须用long long在printf里用%lld格式输出。这个知识点第5题不踩后面也一定会踩。刷题的时候养成“先看题目数据范围再决定数据类型”的习惯是这道题留给你的真正财富。我当时处理循环题还有一个心得不要一上来就写代码先在纸上把循环变量的起始值、结束条件、步长写清楚。从1到n到底是i1; in; i还是i1; in1; i结果是一样的但逻辑上一个干净一个别扭。好的循环结构是你一眼扫过去就能确定“边界绝对准确”的而不是靠反复试。3. 可直接抄的参考代码从第1题到第5题下面我按自己常用的C语言风格把这一类入门题的通用参考实现写出来。每一个代码块我都会做注释说明不只是“给你代码”更希望你理解每一行存在的理由。这些代码在东华OJ的1-5类似题型里直接套用没有太大问题。3.1 第1题参考AB基础版#include stdio.h int main() { int a, b; // 从标准输入读取两个整数中间用空格或换行隔开都可以 scanf(%d %d, a, b); printf(%d\n, a b); return 0; }这段代码没什么好讲的但有几个细节要提醒scanf里的“%d %d”中间的空白字符空格、换行、制表符都是可选的也就是说你用空格分开、换行分开都行。printf里的“%d\n”那个\n一定要带。OJ的评测系统在比较输出的时候通常会把行尾空格忽略但不同题目对换行有要求所以统一在每行输出后加一个换行是最稳妥的做法。3.2 第2题参考处理多组输入直到EOF#include stdio.h int main() { int a, b; // EOF就是End Of File文件结束标志 // 当scanf成功读取两个整数时返回值为2失败时返回EOF while (scanf(%d %d, a, b) 2) { printf(%d\n, a b); } return 0; }这个写法比while(scanf(...) ! EOF)更严谨一点因为它明确要求“成功读到了两个值”才进入循环。如果测试数据里如果有些行残缺用2的判断方式程序不会做出诡异的输出。我在实际刷题中还发现有些同学会在循环外面额外定义一个变量来接收值比如先scanf一次再来一个while这样当然也行但写法不如直接判返回值干净。3.3 第3题参考格式化输出尽量用printf#include stdio.h int main() { double c; // 假设题目要求输入摄氏温度输出华氏温度保留2位小数 scanf(%lf, c); double f c * 9.0 / 5.0 32; printf(%.2f\n, f); return 0; }这道题的核心就两点第一double对应的scanf占位符是%lf不是%fprintf里%f通用但scanf里必须区分第二格式控制里的%.2f表示保留两位小数。我遇到过有人写printf(%.2lf\n, f)在不少OJ平台也能过因为printf对double和float的占位符都接受%f系列但为了规范建议按printf里用%fscanf里用%lf来记。这里另外一个容易出错的点是公式里“9.0/5.0”和“9/5”的区别。整数9除以整数5在C语言里结果是1不是1.8这是C和很多静态语言里特别容易犯的错。所以涉及浮点数计算时至少要让其中一个操作数是浮点数类型。3.4 第4题参考用else if构建互斥分支#include stdio.h int main() { int score; scanf(%d, score); if (score 90) printf(A\n); else if (score 80) printf(B\n); else if (score 70) printf(C\n); else if (score 60) printf(D\n); else printf(E\n); return 0; }因为用了else if程序从第一个条件往下匹配一旦命中就结束判断不会出现一个分数输出多个等级的情况。逻辑上这比“多个独立if”要安全得多。唯一要想清楚的地方是“边界属于谁”——90分走第一个分支80分走第二个分支这种左侧闭区间、右侧开区间的划分方式只要保持连贯就行。3.5 第5题参考循环求和要选对数据类型#include stdio.h int main() { int n; long long sum 0; scanf(%d, n); for (int i 1; i n; i) { sum i; } printf(%lld\n, sum); return 0; }这个代码最大的亮点就是sum用long long。很多人刚学循环喜欢int sum 0到n100000时结果就崩了输出一个乱七八糟的负整数。这就是整型溢出数据太大装不进小小的int容器最高位变成符号位结果就变成负数。用long long之后能装到九百万亿的量级入门刷题足够用。另外循环变量i用int就够了因为n本身可能只是10万级不需要滥用long long保持代码的精确性反而更重要。4. 提交过程中最容易踩的坑一份实测避坑手册代码本身不难真正让新手卡住的是那些“非算法类”的错误。我把自己见过、踩过、帮人debug过的问题集中列一下。这些问题只要有一次印象以后能少吃好多亏。4.1 编译错误IDE能跑OJ编译不过最经典的场景是你在Visual Studio里写完代码运行完美提交到东华OJ提示Compile Error。原因大概率是VS支持某些非标准写法或者你的代码里混入了中文标点。我见过一个师弟代码里有一行printf(%d\n“看起来没问题但仔细看那个右双引号是全角的”编译直接挂。这事的排查方法很简单报错的时候点开编译错误详情看它提示在第几行双击跳过去逐字符检查有没有非ASCII字符。另一个常见的CE问题是头文件。有人用#include bits/stdc.h这个头文件在GCC环境下能用但在某些严谨的OJ平台上不一定被支持。东华OJ我不太确定它让不让用但稳妥起见入门阶段建议按C标准来用#include stdio.h或#include 这种标准头文件。求稳不花哨是刷OJ的基本素养。4.2 答案错误样例过了但还是WA这种情况最让人沮丧。代码在本机跑样例数据全对但提交就是WA。我的排查顺序是固定的第一检查输出格式包括空格、换行、大小写。题目要求输出“Yes”你输出“YES”就是WA。题目要求一行一个结果你用空格隔开也是WA。第二检查多组输入的循环处理。是不是只处理了第一组第三重新看一遍数据范围是不是有int溢出的坑。第四检查边界值比如循环条件是i n还是i n差一个数就全部偏掉。如果你这三步都查不出来那就放弃硬看回到“构造极端数据”的路子上。自己按题目的最大范围生成一组输入把程序跑一遍看输出是不是你手算的预期结果。很多时候WA的问题是题目的隐藏数据里有一个“n0”或“a0”的边界而你的程序没有特殊处理这个情况。4.3 运行错误程序直接崩了RE的原因很多但对入门题来说最常见的是“数组越界”。如果你在前5题里的某道题用到了数组比如声明int a[10]但循环里写到a[10]那就算越界运行时可能报RE。还有一个很容易被忽略的情况除零错误。当题目里可能出现分母为0的边界输入时你的代码没有判0就会直接崩。我给自己定了一条方案凡是出现除法先问自己分母有没有可能是0凡是用数组先问自己下标范围有没有可能越界养成这个反射习惯以后写代码的“防御性”会高很多。4.4 超时入门题也能TLE第5题不太容易TLE但如果你在第1-5题里用了一看就很笨的写法——比如在循环里反复做多余的格式化输出或者用嵌套循环处理本来可以一次循环解决的问题——那么测试数据一大时间就不够了。解决TLE的思路不是去优化“几个微秒”而是改算法结构。不过在前5题这个阶段你大概率不会设计出什么复杂算法只要做到“不要用一个循环包着一个循环遍历所有输入”就够了。5. 从第1题到第5题怎样才算真正吃透很多同学刷题有一个致命误区AC了就立刻下一题代码长什么样自己都忘了。我恰恰认为入门题的做题收获不在于“过了”而在于“把代码和知识点挂上钩”。我的验收标准是这样的你照着自检一遍第一不看任何参考能不能默写出这5道题的完整代码如果你还需要想半天scanf的格式那说明输入输出的肌肉记忆还没形成。第二能不能说清楚每组输入的处理流程比如第二题的多组输入循环条件为什么是2而不是!EOF第三能不能指出你的解题代码中“最可能WA的部分”在哪找不到说明你对题目的理解还停留在“跑通”的阶段。如果上述三个问题你都能流畅回答那就可以大胆进阶了。吃透前5题意味着你已经掌握了顺序、分支、循环、格式控制、多组输入这五个基本结构。接下来你刷树、刷图、刷动态规划的时候才能真正把注意力放在“算法思路”上而不是天天给scanf补坑。我自己带人刷题时还会让他们做一件额外的事把AC的代码重新“写丑”一遍制造一个WA版本然后对比两个版本说清楚是什么导致判题结果不同。这个方法特别培养调试思维。比如你把long long改成int提交一次再把正确版本提交一次亲眼看到同一份逻辑因为数据类型不同而结果翻车比什么理论都有说服力。6. 后续路线过了前5题下一步该怎么走前5题只是把“能跑”的门槛跨过去离“会做题”还差很长一段路。我的建议路线是继续按专题刷东华OJ的基础题而不是随机乱跳先刷一些纯模拟题比如日期计算、字符串处理。这些题不考难算法考你对语言细节的熟练度比如字符数组、strlen、strcmp、字符串和数字的转换。这一关过了你对C语言常用库函数的熟悉度会大幅提升。接着刷查找和排序的题把冒泡排序、选择排序、二分查找这些基本功掌握好。排序是后面很多算法的基础。我见过有人直接调库函数qsort或者sort这固然没问题但我依然建议你先手写一遍简单排序搞清楚排序过程的每一步再用库函数提高效率。这是为了以后理解“为什么快排平均复杂度低”“为什么某些变态数据会让快排退化”做铺垫。然后进入递归专题比如汉诺塔、全排列、斐波那契数列。递归是很多同学的第二道坎它的核心不是“函数调用自己”这个动作而是“把大问题拆成结构相同的子问题”。如果刷到递归相关题目时心情很痛苦别慌那说明你在突破再坚持一下。等你把模拟、排序、递归这些基础专题都过完再去接触栈、队列、链表以及更后面的搜索和图论就不会觉得前路迷茫。我个人认为算法刷题的时间和难度分布是“越往后越陡”但前期的地基打牢了后面的陡坡就只是坡不致命。拿我自己来说我重新刷完这套1-5题之后最大的感受不是“题目简单”而是“原来很多错误信号可以被预判”。OJ报告WA之前我心里其实已经有预感知道这道题的边界情况我没处理好。这种预判能力是刷题刷出来的直觉。希望这篇文章能帮你少走一些弯路至少让你看到“WA”的时候想的不是“这破机器有病”而是“我的程序还存在哪个测试点上没有覆盖到”。带着这个思维东华OJ后面两千道题都可以平趟。