ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

东华OJ基础题69-73全解析:数组、数字拆分与调试技巧

东华OJ基础题69-73全解析:数组、数字拆分与调试技巧 说来也巧每年到这个阶段总有学弟学妹跑到我这儿问同一个问题东华OJ的基础题刷到六十几题了卡在69到73这一片代码写了、样例也过了一提交就是红红的WA心态直接崩掉。我当年也是从这条路上趟过来的所以今天专门写一篇把这五道题掰开揉碎聊一聊。不管你卡在哪一题、是C还是C选手、甚至用Python刷这份思路都适用。先说清楚东华OJ是什么。它是东华大学在线的编程评测系统题目按难度和专题排列前面六十多题基本把输入输出、分支循环、数组字符串这些基础语法都过了一遍。做到69到73题这个位置意味着你已经跨过了“能写代码”的门槛开始进入“用代码解决具体问题”的阶段。这组题我不能把原题照抄出来但可以负责任地讲它们都属于一个典型专题——数组、数字处理和循环模拟的综合应用。这篇文章会用这五题的常见考法做样例把每一类题的分析思路、代码写法和调试技巧全部分享出来。1. 东华OJ基础题69-73内容定位与考察方向1.1 从题号安排看这组题的定位东华OJ的题号顺序基本和知识点推进顺序一致。前面几题考顺序结构、简单计算中间开始考分支和循环到了60到70这个区间数组正式登场。你可能已经注意到从这附近开始题面变长了不再是一句话讲完“输入一个数输出一个数”而是给你一段完整的场景描述比如“输入n个整数按要求处理后输出”。69到73这五题在这个区间里扮演的角色很特殊。它们不是纯数组题也不是纯数学题而是把数组、循环、条件判断、数字拆分这些东西混在一起考。这正是OJ出题最喜欢的手法和真实场景下写代码最常遇到的情况——你很少会只用一个知识点解决一个完整问题。我翻了一下自己做题时候的记录和跟别人对题目的讨论这五题里出现过的知识点主要集中在这几个方面从一行输入中读入多个整数并存入数组对数组进行遍历、筛选、统计把一个多位数拆成各个数位再重组在循环里维护一个“当前最值”或者“累计结果”。这些东西单独拿出来你基本都会但合在一起、再加上OJ严格的格式判定就成了新手杀手。1.2 基础题背后真正想考的东西说实话东华OJ这五道题本身的算法难度并不高任何一道放到竞赛里都属于送分题。但为什么这么多人在这一片卡住因为从这一组题开始OJ不再考你“语法会不会”而是考你“思路清不清楚、代码严不严谨”。举个例子数字拆分这类题很多人第一反应是用字符串处理读一个整数转成字符串再一位一位取。这个思路在本地跑完全没问题但东华OJ这类题目的输入约定往往是“每行一个整数可能包含多行”而且数字范围是int范围内字符串处理反而容易在格式和转换上出幺蛾子。更稳的做法是用除法和取余运算把每一位抠出来这也是这一类题目默认的标准方法。这五题集中训练的是三种能力。第一把题目描述翻译成变量和操作的能力比如“统计个位数为某个数的数字个数”这句话你要能立刻想到用n % 10判断个位第二循环边界控制能力比如“共T组数据”和“读到文件末尾EOF”是两种不同的输入方式对应完全不同的循环写法第三输出格式的强迫症级严谨度空格、换行、最后一个数后面要不要空格这些细节直接决定你能不能AC。2. 解题前的准备工作读题、设计、复杂度估算2.1 三遍读题法样例不是摆设我看到太多人做OJ题目的姿势不对题目扫一眼觉得看懂了直接开写写完拿样例跑一下对了就交错了就懵。这种做法在基础题阶段还能靠运气混过去到了69-73这种综合题十有八九要栽。我自己的习惯是三遍读题法这么多年带人刷题一直推荐给新手反馈很好。第一遍快速浏览题面只搞清楚三个问题输入是什么、输出是什么、中间要做什么操作。这一遍不追求理解所有细节但要能用自己的话把题目复述一遍。第二遍从头精读重点关注约束条件和边界描述。比如“正整数”意味着你不用处理负数和零“n不超过100”意味着你甚至可以用最暴力的方法“如果不存在则输出-1”这种话就是典型的边界陷阱很多人漏看的都是这种补丁说明。第三遍拿着样例手动模拟。把样例输入里的数据按你理解的流程在草稿纸上走一遍看能不能得到样例输出。这一步是性价比最高的排查方式。你代码还没写思路里的逻辑漏洞就已经暴露出来一大半。我见过一个特别典型的例子一道题要求“输出排序后的结果每个数之间用一个空格隔开”样例输出是1 2 3看起来人畜无害。但提交后WA了十几次最后发现题目在输出格式里写的是“行末不要有多余空格”而他的代码在最后一个数后面也打了个空格。这就是样例不会告诉你、但评测系统一定会检查你的地方。2.2 复杂度心里要有数“这题我能不能暴力解”是每个刷OJ的人都问过的问题。答案是看数据范围。东华OJ基础题的n通常非常小几十到几百的量级O(n²)的算法跑起来毫无压力。但这不代表你可以完全不顾算法效率因为这一片题目里经常出现“多组输入”的情况。比如一道题说“输入数据有多组每组第一行为一个正整数n”但没有告诉你一共有多少组。如果单组内你做的是O(n²)的冒泡排序n只有100那没问题可如果题目没有明说n的范围或者n能到10的5次方甚至更大O(n²)就会超时。我给新手的建议是写代码之前先花三十秒估算一下最外层循环大概执行多少次、里面还有没有嵌套循环、总的计算量在什么量级。如果总量超过一亿次就得思考能不能优化。基础题阶段你大概率用不到什么高级算法但“用桶计数替代双重循环查找”这种技巧在数组统计类题目里非常常见69-73这组题里就有类似的考法。另外别因为数据弱就放飞自我。养成计算复杂度的习惯等到后面刷中档题的时候你会感谢自己现在多花的这三十秒。3. 核心知识点逐个击破3.1 数字拆分与重组基础中的基础数字拆分是东华OJ从基础到进阶反复出现的知识点69-73里基本一定会涉及。所谓数字拆分就是给定一个整数把它的个位、十位、百位……一位一位分离出来做处理。标准做法是用取余和整除循环#include stdio.h int main() { int n; while (scanf(%d, n) ! EOF) { int sum 0; while (n 0) { sum n % 10; // 取出当前最低位 n / 10; // 去掉最低位 } printf(%d\n, sum); } return 0; }这段代码做的事情很直观n % 10拿到当前个位n / 10把个位砍掉循环直到n变成0。比如n12345过程就是取出5、4、3、2、1最后sum15。实际操作中有三个细节容易翻车。第一如果输入的n本身就是0上面这个循环一次都不会执行sum就是0这个没问题。但如果题目要求“逆序输出数字”n0时你应该输出0而不是什么都不输出这时候就要用do-while而不是while。第二负数的取余在不同语言里行为不一样C语言里-5 % 10结果是-5所以如果题目里没说n是正整数最好先把负数转成正数处理或者用绝对值。第三如果题目要求把拆出来的数位再重组比如“把各位数字逆序排列后输出”你要注意前导零的问题比如1020逆序变成0201输出的时候要不要保留0完全取决于题目描述。3.2 数组存值、统计与最值处理到了69-73这组题数组操作是躲不开的。常见的考法有这么几类读入n个数存入数组然后做某种筛选或者直接不存数组边读边处理。这两种思路的效率差别不大但代码复杂度差别很大。给你一个建议需要“先全部读入再统一处理”的题老老实实开数组存可以“边读边算”的题就别浪费内存。比如求一组数的最大值和次大值其实不需要数组#include stdio.h int main() { int n, i, x; while (scanf(%d, n) ! EOF) { int max1 -1, max2 -1; // 假设数据都是非负数 for (i 0; i n; i) { scanf(%d, x); if (x max1) { max2 max1; max1 x; } else if (x max2) { max2 x; } } printf(%d %d\n, max1, max2); } return 0; }这段代码的巧妙之处在于只用两个变量就完成了最大值和次大值的维护。每一步更新的时候如果当前数比最大值还大原来的最大值就“降级”成次大值如果当前数介于两者之间只更新次大值。这个过程实际上就是一次遍历里同时维护两个排序状态比“先存数组再排序再取前两个”的要快而且不需要额外空间。另一种高频考法是“统计类”比如统计每个数字出现的次数。这种题最标准的解法是桶计数开一个数组下标当数字本身数组值当次数。比如统计n个1到100之间的整数中每个数出现了几次int count[101] {0}; for (int i 0; i n; i) { scanf(%d, x); count[x]; }这个思路之所以重要是因为它在O(n)时间内完成了统计而如果每次查询都去遍历原数组复杂度就是O(n*m)数据稍大就会超时。桶计数的本质是用空间换时间在数据范围有限的时候非常实用。3.3 循环嵌套与多组输入的写法东华OJ从基础题开始就很喜欢考“多组测试数据”69-73也不例外。很多在这五题上翻车的人问题不是出在业务逻辑上而是连最外层输入循环的写法都没掌握。最常见的两种输入模式你要烂熟于心。第一种题目明确说“第一行是一个整数T表示测试数据的组数”这种写起来最简单int T; scanf(%d, T); while (T--) { // 处理一组数据 }第二种题目说“输入包含多组测试数据处理到文件末尾”这种要配合scanf的返回值来判断int n; while (scanf(%d, n) ! EOF) { // 处理一组数据 }这里的关键是理解scanf的返回值它返回成功读取的变量个数读不到数据时返回EOF即-1。所以while (scanf(...) ! EOF)的意思就是“只要能读到数据就一直处理”这样就不需要用户手动输入一个终止标记。还有一个非常常见的坑多组数据之间变量没有重置。比如求每组数据的和如果你把sum定义在while循环外面又没有在每组开始时清零那第二组数据的和就会把第一组的结果累加进去结果自然错得离谱。记住一条铁律任何“每组数据独立”的变量都要在循环体内部定义或者每轮循环开始前初始化。4. 判题反馈与调试实录4.1 常见判题结果到底在说什么提交代码之后OJ会返回一个判题结果。很多新手看到红字就慌其实每一种结果的含义完全不同排查方向也是天差地别。我按东华OJ常见的几种反馈给你列一张表。判题结果含义重点排查方向Accepted代码通过不用排查做下一题Wrong Answer答案错误算法逻辑、边界条件、输出格式Runtime Error运行时错误数组越界、除零、递归过深Time Limit Exceeded超时算法复杂度太高、死循环Compile Error编译错误语法问题、头文件缺失、函数名拼写Presentation Error格式错误空格、换行与要求不一致这里我重点讲一下Presentation Error因为这道题区间里太容易触发了。PE和WA的区别在于你输出的内容在数值上是对的但输出格式和题目要求不完全一致。比如多打了一个空格、少打了一个换行、行末多了个空格。我当年带过一个学弟一道“输出n个整数之和”的题他犯的错是每组数据之间多打了一个空行。他自己看输出觉得“差不多”但OJ不认识“差不多”它拿你的输出和标准答案逐字符比对一个字符不一样就是WA或者PE。所以写输出的时候一定要跟题目要求的字节级格式对齐。4.2 我是怎么定位“答案错误”的WA是刷OJ路上最常见的反馈也是新手最难解决的因为OJ只告诉你“错了”却不告诉你“哪里错了”。在处理69-73这几题时我的排查套路已经固定下来了你照做能省一大半时间。第一步重新读题。不是扫一眼是精读。重点看三块数据范围有没有限制比如是否可能为0、负数、输出格式有没有特殊要求比如“行末无空格”、“每组数据后跟一个空行”、有没有隐含条件比如“按输入顺序输出”而不是“按从小到大输出”。第二步自造边界测试数据。样例给的数据往往很温和你要自己制造极端情况。比如n1时能不能过n100且所有数都一样时能不能过最小值和最大值出现时能不能过我之前帮人调一道统计题样例全过但n1时直接崩了因为代码里假设了“至少有两个数”才去求次大值。这种问题只有边界测试能暴露出来。第三步打表调试。在关键位置加printf输出中间变量把程序的实际执行过程“看”一遍printf(debug: i%d, x%d, max1%d, max2%d\n, i, x, max1, max2);看完之后记得删掉这些调试代码不然这些多余的输出会让你直接WA。第四步如果你的逻辑实在看不出破绽重新审视“每轮循环变量重置”问题。这是多组输入题目里最隐蔽的坑。我自己的经验里至少有三分之一帮别人排查的WA最后都是“某个变量忘了清零”导致的。4.3 提交前必查清单我在东华OJ刷到后面养成了一个习惯写完整段代码之后先不急着交按一份固定清单自查一遍。这套习惯帮我减少了很多无效提交。清单内容如下。数组开得够不够大如果题目说n最大1000就开1005而不是1000留一点余量防越界。变量是否初始化全局变量默认是0但局部变量默认是随机值局部变量不初始化是新手最常见的RE来源。循环条件是否正确是i n还是i n是while (T--)还是while (T)这种差一个字符的bug最难查。scanf有没有加给int变量读值漏了在本地可能碰巧不崩但在OJ上几乎必RE。输出格式是否逐字核对空格、换行、冒号、逗号全都要跟题目要求一致。多组数据的变量重置是否完成每一轮循环开始时的状态是否干净。int会不会溢出如果累加结果可能超过21亿就换成long long。这套清单看起来都是小细节但OJ就是这么不留情面一个字符的差异就能让你的通过率从100%变0%。5. 新手排错速查与避坑心得5.1 高频问题对照表把69-73这组题里我见过、以及我自己踩过的高频问题汇总一下做成速查表提交WA的时候拿出来对照效率很高。症状可能原因解决方案样例能过但提交WA边界条件没处理补测n1、n最大值、全同数据等边界多组数据时答案越算越离谱累计变量没清零把sum、count等变量移到循环内定义输出结果正确但格式被打回来行末空格或空行不匹配核对题目输出说明最后一个数不打印空格运行直接崩数组越界检查数组大小确认下标从0还是从1开始结果全是同一数值忘了读入新数据检查scanf是否写在了循环里面且每次执行所有输出都比预期大一点多个样例累计没清空每轮处理前把结果变量重置为初始值这里的每一条都是我或者我带过的人真真切切踩过的。以前总觉得“这种低级错误怎么会犯”真到OJ上熬夜调代码的时候才发现低级错误反而是最消耗时间的。5.2 几条专门讲给新手的经验最后分享几条我刷东华OJ多年的经验算不上什么高深技巧但都是实战里反复验证过的。第一本地编译器能跑不等于OJ能过。本地你的代码可能依赖了一个没有初始化的变量的“幸运值”这在你的电脑上碰巧是0在OJ的编译器上就成了随机数。所以写代码的时候别依赖任何未初始化的状态老老实实把变量都赋值。第二代码能写出来之后花一分钟手动推演一遍。这一步不能省尤其是循环嵌套的题目。你把第一次循环、最后一次循环分别手动跑一遍能抓住80%的边界问题。我教过的一个学生每次写循环都会差一个数后来我强制他每次写代码都推演首尾两次循环这个问题基本绝迹。第三调试用的printf一定要删干净。这不是玩笑。有一次我调完代码忘记删调试输出交上去WA盯着判题结果发呆了十分钟才反应过来。建议把调试代码统一加一个特征标记比如printf(DBG...)最后全局搜索“DBG”一次清干净。第四如果你实在卡在一个题超过两小时别硬扛。去洗把脸、走一走或者换一道简单题做做给自己二十分钟的缓冲。很多次我都是离开了屏幕再回来突然想起“啊那个数据可能是多组”一下就解决了。卡题的时候最怕钻牛角尖走出去反而能让大脑切换状态。东华OJ基础题69-73这五道题难点不在算法而在“严谨”二字。数据怎么读、循环怎么控制、格式怎么输出、边界怎么防护每一环都严丝合缝才能换来一个绿色的Accepted。把这五道题吃透你不仅是拿到了几道题的AC记录更重要的是建立了面对OJ题目的系统化排查思维。这套思维往后做任何题目都用得上包括后面那些真正的算法题。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表