尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

OJ刷题第二天:基础题04-06的解题套路与提交避坑指南

OJ刷题第二天:基础题04-06的解题套路与提交避坑指南 很多刚开始刷OJ的朋友都会卡在同一个地方题解看懂了别人的代码也贴得出来可一旦关掉题面自己从头写就浑身难受。更典型的情况是第一天兴致勃勃刷到半夜第二天打开题库面对“04、05、06”这几道基础题突然不知道该先干嘛、该注意什么、提交之后返回的WA到底错在哪。这篇就专门聊“第二天基础OJ 04-06”这件事。表面上看只是三四十行的小题实际上它们是把第一天学到的输入输出、条件判断、循环拿去做“实战检验”的第一步。我会把这些题的通用解法、提交评测的机制、排查错误的思路全部拆开讲顺便给出一份可以直接照抄的做题模板。就算你手头题库里的具体题目和我说的不一致这套分析框架同样能套用。1. 从“第一天”到“第二天”OJ刷题计划该怎么排1.1 为什么是“第二天”这个时间节点OJ刷题路径里“第二天”是一个特别微妙的位置。第一天大家通常在做环境配置、熟悉提交页面、跑通AB这类最基础的输入输出题处于“原来代码是这么交上去的”状态。而到第二天题目开始加入选择结构和循环难度没到算法级但又比“直接打印”高了一档。很多人的第一个放弃点就在这里不是题目难而是挫败感来得太快。写出来的代码在自己电脑上运行完全正常样例输入输出的结果一模一样可一提交就是Wrong Answer。这种落差最容易让人怀疑自己。所以我一直觉得第二天刷题的目标不应该是“刷完多少道”而应该是“彻底弄懂OJ判题到底怎么判”。如果你也在按类似“基础OJ 04-06”的顺序刷题建议第二天只做三件事第一把输入输出各种格式踩一遍尤其是有多组数据要处理的读法第二把选择结构、循环结构的基本套路练熟做到不用看笔记能直接写出框架第三学会读OJ返回的状态WA、TLE、MLE分别代表什么别再说“它就是报错”这种话。1.2 题目编号“04-06”的常规定位不同学校的OJ平台题目前几道往往都是按知识点排的。综合信阳师范大学、郑州轻工业大学、杭电这类常见平台的入门题库来看第04到06题基本集中在下面三个方向字符或数字的条件分类、简单数学公式计算、带循环的累加累乘。这三类题最大共性就是“逻辑上绕一小下但代码量很少”。以我当年刷题的经验最典型的第4题是“输入一个成绩输出等级”这类选择结构题第5题往往是“求1到n所有满足某个条件的数之和”第6题则可能升级成“多组输入每组输出一个结果”。这三道题如果都独立完成第二天的基础目标就算达成了。但注意这里说的“完成”不只是通过样例数据而是能提交变成Accepted。1.3 明确这三道题的价值定位能把“基础OJ 04-06”吃透的人后面做中等难度题会顺畅很多。因为这三道题虽然简单它们覆盖的却是整个OJ生涯最常用的三大基础能力读对输入、算对逻辑、输出格式一个字符都不能差。这三点里任何一点出毛病编程水平再高也照样拿不到AC。所以我对新手的建议是不要小看这几道基础题也别急着往后跳。后面你写贪心、写搜索、写动态规划本质上都是在“读数据、做计算、输出结果”这个框架上堆复杂度。现在花半小时把这三道题做扎实后面能省下好几个小时的排错时间。2. 三道基础题的思路拆解与上手指南2.1 一道典型的选择结构题该怎么做拿“成绩等级转换”这类题举例题面一般是90分以上为A80到89为B70到79为C60到69为D60以下为E。看起来没有任何难度但新手提交时最容易栽在“边界值”上。比如89分算B还是算A60分算D还是算E题目里用的是“以上”“以下”“含”“不含”这直接决定你的if条件是大于还是大于等于。我的建议是凡是遇到分段条件的题先把边界值在草稿纸上列出来像这样90、89、80、79、70、69、60、59。然后逐个检查你的条件能不能覆盖到。比如写成if(score 90)和else if(score 80)这种阶梯式写法就天然比if(score 80 score 90)更不容易漏边界。这类题真正的分水岭在于如果输入可能出现不在0到100之间的非法数据题目有没有保证输入范围。OJ题一般会在题面写明“输入保证为0到100之间的整数”没写的话你就要考虑是否做合法性校验。不建议新手一上来就在这种细节上抠太久但心里要有这根弦。2.2 循环求和类题目的核心注意点第二道典型题是求和比如求1到n之间的所有奇数之和或者求某个数的阶乘。这类题的代码框架很简单一个循环变量一个累加变量循环体里做累加。但新手往往会犯一个经典错误——累加器没有初始化。如果忘记把sum赋为0它的初始值是不确定的最终结果自然也不对。再往前走一步题目可能会要求“求1到n之间能被3整除但不能被5整除的数的和”这就把“循环”和“条件”结合起来考。做这类题先别急着写代码先在纸上把流程捋一遍n为10时应该取哪些数手算结果是什么。这个手算结果就是你提交后的期望输出也是你排查逻辑错误的基准。还有一种变体是“多组测试数据”每组给一个n分别输出求和结果。很多新手在这里会被卡住因为他们不知道OJ题目的“多组输入”一般写作while (scanf(%d, n) ! EOF)或while (cin n)。这个读法必须死记硬背熟练因为在入门题里出现频率实在太高了。另外注意多组输入的题每组输出通常要换行这跟单组输入的写法完全不同。2.3 字符串或格式输出型题目的额外讲究06号题如果出了字符处理常见的有“输入一个字符判断它是字母、数字还是其他字符”。这类题会用到字符比较理解char在内存里实际存的是ASCII码值这个事实做起来就顺了。比大小本质上是在比ASCII码值。更麻烦的是带格式的输出题比如保留两位小数、占位符宽度、输出字符图案。这种题考察的已经不是算法而是对printf格式控制的熟练程度。printf(%.2f, x)代表保留两位小数%5d代表占5个字符宽度右对齐这些小细节是OJ题里最容易丢分的点。写上代码后反复比对样例输出里的空格数、换行位置这是唯一的笨办法也是最有效的办法。2.4 我建议的上手顺序如果在计划表里今天要刷“04-06”三道题我推荐按“先易后难、先逻辑后格式”的顺序来做。先做只有条件判断的题痛痛快快拿到一个AC给自己建立信心再做需要循环累加的题体会状态变量怎么更新最后再做带格式输出或者字符处理的题把最容易出细节问题的题目留到体力最好的时候。这个顺序还有一个额外好处前三题AC之后你已经熟悉了这套“读题、编码、提交、排错”的流程最后一道题就算卡住心态也更稳。刷题最怕的不是不会而是手忙脚乱时连报错信息都没耐心读。3. OJ评测机制与提交细节读懂才算入门3.1 黑盒测试到底是怎么跑的要搞清楚为什么“本地正确”但“提交错误”你首先得明白OJ到底拿你的程序做了什么。简单说OJ会把你的代码拿去编译然后用预设好的输入文件喂给你的程序再拿你的程序输出和标准答案做“逐字节对比”。也就是说OJ完全不关心你的程序内部写得好看不好看、变量名是否语义化、效率到底多高它只看输出结果。这里最震撼新手的一点是哪怕你的程序逻辑完全正确只要多打了一个空格或者最后多了一个换行一样判你错。这叫Presentation Error或者Wrong Answer。举个例子假设标准输出是a1你的程序输出a1末尾多一个空格有的平台会提示Presentation Error有的平台会直接算WA。这听起来很残酷但这就是OJ规则。所以我一直推荐新手学第一门语言时就把printf的格式控制学好宁可多写几行代码也把每个输出字符抠清楚。3.2 常见提交状态一览这里把OJ最常见的返回状态整理成一张表建议收藏备用。状态缩写英文全称含义与常见原因ACAccepted答案正确程序输出与标准答案完全一致PEPresentation Error输出格式有误比如多了空格、空行WAWrong Answer输出结果错误程序逻辑或边界情况有问题TLETime Limit Exceeded超时程序运行时间超过限制通常指算法太慢或有死循环MLEMemory Limit Exceeded内存超限程序申请的内存超过题目的限制RERuntime Error运行时错误比如数组越界、除零、非法访问CECompile Error编译错误代码本身有语法问题OLEOutput Limit Exceeded输出超限程序输出了大量多余内容常伴随死循环我见过太多新手拿到WA之后完全摸不着头脑连“WA到底是什么意思”都不知道直接把代码删了重写。其实WA只是告诉你“输出和预期不一致”你的算法思路可能是对的只是某个边界值算错了。学会区分这些状态你排错时至少能知道往哪个方向想。3.3 本地运行和OJ运行的差异很多人用Dev-C、VS Code或者其它IDE写完代码在本地测试样例通过了就认为万事大吉。但实际上本地环境和OJ环境至少有三大差异。第一编译器版本和标准不同。你在本地用C17编译OJ可能还在用C11甚至C99标准某些语法或函数库就会编译失败。第二文件读写方式不同。部分OJ要求从标准输入读、往标准输出写而不是读写文件如果你的代码里写了freopen去读文件本地有文件当然没问题交上去就必挂。第三数据范围更大。样例数据往往只是测试数据的冰山一角比如题目说n最大可以到10^6样例里只给了n5那你的算法在n10^6时必须仍然能工作。我对新手的建议是刷OJ时一律使用最简单的scanf/printf或者cin/cout从标准输入输出读写不要依赖任何限定本地环境的操作。提交前也要养成习惯看看题目给的数据范围估算你这个算法在最坏情况下要跑多久。3.4 main函数的返回值规范这点超级基础但超级重要OJ判断你的程序是否正常结束很大程度看main函数有没有返回0。如果不写return 0一些编译器会隐式补上但有些环境就是会出问题。刷题时所有代码main函数末尾请统一写return 0;。此外别用void main这种写法标准C/C里main函数返回值类型是int。用void main在本地可能编译通过某些OJ直接CE这种扣分完全没意义。代码规范这东西入门时养成好习惯能省掉一堆莫名其妙的错误。4. 实战环节把完整刷题流程走一遍4.1 以“平方差数”为例的完整解题过程热词里提到了“xtu oj 1103 平方差数”我们就以这类题为例完整走一遍从读题到AC的流程。假设题面是“判断一个正整数n是否可以表示为两个正整数的平方差即n a² - b²其中a、b为正整数且a b”。先做数学推导这是典型的基础数论题。平方差公式是a² - b² (a b)(a - b)。设a b pa - b q那么p和q同奇偶而且p q 0n p * q。也就是说只要n能分解成两个同奇偶因数的乘积就一定存在对应的a和b。这里有一个重要结论任何大于1的奇数都可以表示为平方差。因为奇数n可以分解为n * 1n和1同为奇数于是a (n 1) / 2b (n - 1) / 2。而能被4整除的偶数同样可以例如12可以写成4 * 3不对4和3不同奇偶这里要分解为2和6同偶所以12 6² - 4² 36 - 16 20也不对。我们直接推n为偶数且能被4整除时n 2 * (n/2)2和n/2同为偶数所以同类但如果n只是奇数乘以2得到的比如6 2 * 32和3不同奇偶则无法表示成平方差。所以这道题的判断条件会化简成n是奇数或者n能被4整除。这样的数学推导是这种题目的核心。写代码就很简单了#include stdio.h int main() { int n; while (scanf(%d, n) ! EOF) { if (n % 2 1 || n % 4 0) { printf(Yes\n); } else { printf(No\n); } } return 0; }大家可以看到题目看着像要你找a和b但实际上推过数学公式后代码就变成了一个非常简单的判断。这也是OJ入门题的一种典型套路看起来是编程题其实考的是数学观察能力。所以拿到这种题第一件事不是开IDE而是拿纸笔推一推。4.2 提交前的本地测试策略代码写完之后不要急着交。先在本地把你能想到的边界情况全部测一遍。对上面这道题至少要测这些输入n为1、2、3、4、5、8、9、12。手算结果分别是1是奇数Yes2不是奇数也不能被4整除No3是奇数Yes4能被4整除Yes5是奇数Yes8能被4整除Yes9是奇数Yes12能被4整除Yes。把这些结果和程序输出逐一比对没问题了再提交。这个过程叫“构造测试样例”是程序员的基本功。你以后做项目、写接口、搞算法都会用到这个技能。不要觉得小题就不用测基础题恰恰是养成测试习惯最好的机会。4.3 记录问题与踩坑笔记第二天刷题强烈建议开一个文档或者直接在博客里记录每道题的完成状态。我自己的习惯是四栏题目编号、题目类型、AC状态、踩过的坑。比如我刷这类题时会记录“xtu oj 1103数论/规律题AC坑点是多组输入不要用单次scanf”。这个笔记在你后续刷题时会发挥奇效。你三天后回头看会发现很多坑是重复的。比如今天因为多组输入WA了一次下次遇到多组输入就会条件反射地想起这条笔记。学习编程很大程度上就是“踩坑—记录—不再犯”的循环OJ只是把这个循环压缩到了更短的时间周期里。5. 常见问题排查与避坑技巧5.1 死记硬背但超级实用的输入输出模式这里把OJ入门题最常见的三种输入输出模式全部列出建议直接照抄到自己的代码模板里。第一种单组输入输出。这种最简单读一个值算一下输出结果。#include stdio.h int main() { int n; scanf(%d, n); // 处理逻辑 printf(%d\n, n); return 0; }第二种先给一个组数T然后一共输入T组数据。这是最典型的批量题目格式。#include stdio.h int main() { int T, n; scanf(%d, T); while (T--) { scanf(%d, n); // 处理逻辑 printf(%d\n, result); } return 0; }第三种不告诉你有多少组读到文件末尾才算结束。#include stdio.h int main() { int n; while (scanf(%d, n) ! EOF) { // 处理逻辑 printf(%d\n, result); } return 0; }这三种模式不分高下全看题目要求用哪种。我最推荐的练习方法是每做完一题就问问自己“这题如果改成第二种/第三种输入模式我的代码要怎么改”。经常做这个练习之后输入格式将再也不是你的绊脚石。5.2 被WA虐到无语时的排查方向如果你的代码逻辑看起来正确样例也过了提交却是WA按下面顺序逐一排查基本能定位九成问题检查题目是不是多组输入你却只读了一组。这是入门题WA最常见原因。检查输出格式和样例是否完全一致包括空格、大小写、换行、百分号这些细节。检查数据范围int够不够用。n到10^9时求和就可能溢出这时要用long long。检查边界条件比如n0、n1、n取最大值的时候你的逻辑是否成立。检查算法本身有没有考虑全所有情况比如平方差数那道题你只判断了奇数忘了4的倍数那就是WA。有些同学遇到WA会反复改格式、改写法但从不仔细重新读题。我的建议恰恰相反WA之后第一件事是重新读一遍题面很多时候答案就藏在题目原话里。5.3 数组越界和除零这类运行时错误RE在入门题里可能不常遇到但一旦遇到多数是数组越界。比如你开了int a[100]循环却跑到i 200这就是未定义行为但程序不一定崩溃。更可怕的是它可能偶尔正常、偶尔崩溃或者覆盖了其他变量的内存导致结果诡异。调试RE的有效办法是检查每一个数组下标的最大可能值是否超过了数组长度检查除法运算的除数是否可能为0。题目里常说“输入保证m 0”那除数是0的情况就不用考虑如果没说你就要自己在代码里判断。另一个容易忽略的是除零n % 0或者x / y中y为0。这类错误通常会在提交后立刻返回RE。所以做涉及取模、除法运算的题时条件判断里必须先排除分母为0的情况。5.4 超时的初步预防思路TLE在基础题里其实很难碰到因为题目本身计算量不大。但如果你在第5题、第6题就遇到TLE多半是写出了死循环比如while循环里忘记更新循环变量或者多组输入时循环退出条件写错。如果以后做中等难度题碰到TLE那就是真正的算法问题需要从时间复杂度角度思考优化。比如同一个求和问题用for循环是O(n)用数学公式就是O(1)。入门阶段你可以不用深究但要有“程序跑得快慢有量化标准”的意识这就是时间复杂度的雏形。5.5 本地输出正常但OJ显示OLEOLE在中文OJ平台不太常见但遇到一次就印象深刻。通常原因是循环里有printf没挂条件导致程序输出了海量数据。更隐蔽的情况是多组输入的题里每组输出末尾都换行是没问题的但如果你额外打印了一堆提示语句比如printf(请输入一个数)那输出就爆炸了OJ直接给OLE或者WA。所以我反复强调提交到OJ的代码绝对不能有任何与输出要求无关的打印语句。很多人本地调试时喜欢加printf看中间变量提交前必须全部删掉或注释掉。这是一个入门第一天就该记住但很多人到第N天才被教育的问题。6. 那天之后回头看第二天基础题教会了我什么说实话做“04-06”这三道题已经是很多年前的事了。但直到现在我在工作里写代码、做代码审查时很多习惯都还能追溯到那个阶段。比如拿到问题先手算几个例子比如提交前老老实实再读一遍题面再比如遇到诡异bug时先怀疑输入而不要急着改逻辑都不是什么惊天动地的大道理但全是从OJ送上的一次次红色WA里逼出来的。所以如果你现在正卡在第二天的基础题上别急也别觉得自己笨。OJ刷题本质上是一个“被规则反复教育”的过程被教育几次之后规则就变成肌肉记忆了。后面那些所谓的“算法高手”绝大多数也不过是在这个阶段多扛了几个回合而已。把04、05、06这三道题老老实实写明白、测清楚、提交过你就已经迈过了很多人在第二天就放弃的那个坎。后面还有第三天、第四天还会有更复杂的题等着但你已经有了自己的做题套路和调试方法接下来的路会越走越顺。
返回列表