东华OJ基础题69到73这一连续区间在学弟学妹群里被问到的频率一直不低。理由很简单这五道题几乎是东华大一C语言课“从语法到算法”的分水岭前面的题目主要考你“知不知道这个语法”到这里开始考你“能不能把语法组合起来解决问题”。不少人前面刷得飞快到这一组突然卡住一卡就是好几天。我当年也是从这道坎跨过来的所以想把这一组题目的底层逻辑、常见写法还有我反复踩过的坑一次性说清楚给正在刷题的你省点时间。先明确这套题适合谁正在学C语言、刚接触OJ判题系统的大一学生或者自学编程想找一套“有梯度、不劝退”的入门题单的人。它能帮你解决的问题很具体——怎么把循环、数组、字符串、枚举这些零散知识点组合起来写成能通过Judge的代码。它是一块很典型的“承上启下”路标刷透这一组你后面接触递归、排序、结构体时会顺很多。1. 从题目编号看东华OJ的出题规律1.1 69到73这五题在题库中的定位东华OJ的基础题部分编排逻辑其实挺清晰的前60题基本围绕分支、单层循环、简单数学公式展开一题只考一个点。从60多题开始题目开始“混合”知识点比如循环里面嵌分支、数组里面做统计、字符串和循环结合处理。69到73正好处在这个过渡区的最深处所以你会明显感觉到——一看题目都懂一写代码就错错完还很难查。这五题具体考什么我根据刷题记录和带新人的经验做一个大致归类不同年份题单可能有微调数字分解与重组、字符统计与判断、数组筛选与极值、递推数列、简单过程模拟。这些恰恰是后续一切算法题的“基本动作”。比如数字分解是数位DP的雏形递推数列是动态规划的雏形过程模拟则是贪心、搜索类题目的基本功。你在这一组花的时间后面都会连本带利赚回来。1.2 为什么基础题比难题更值得反复做很多同学喜欢一上来就怼难题觉得基础题“没技术含量”。但以我带人刷OJ的经验基础题的价值恰恰在于它的约束少、出错空间大。难题往往框架固定你知道要用排序、要用搜索代码结构摆在那边基础题反而容易暴露细节问题——循环变量初始化位置写错了、数组越界一位、scanf里忘了加、多组输入没清空缓存这些问题在难题里会被复杂逻辑掩盖在基础题里却会赤裸裸地让你WA到怀疑人生。还有一点OJ基础题教会你的并不是“怎么做对一道题”而是“怎么稳定地对很多道题”。这种稳定性靠的是反复训练形成的肌肉记忆而不是临时想出来的灵光一现。把69到73这一组题刷透、刷出感觉你后面再看任何题目第一反应都会从“这题怎么下手”变成“这题属于我熟悉的哪一类”这种思维转变才是做题量积累的真正回报。2. 核心细节解析与实操要点2.1 输入输出格式的规范是第一只拦路虎OJ题和普通练习题最大的区别就是判题程序只认你程序的输出结果不多不少、不差一个空格。很多人代码逻辑完全正确但就是因为输出格式差一个空格被判定WA这种亏我吃过不止一次。69到73这一组的题目大多是单组输入但也会有那种“输入多行数据直到遇到特殊标记才结束”的题目你要做的第一步永远是仔细读题搞清楚输入到底是一行、多行还是不定长。拿数字分解这类题来说如果题目要求“输出每位数字用空格分隔”那么最后一个数字后面绝对不能有空格。我见过不少同学用printf(%d , digit)这种写法一路打到底最后被判WA还百思不得其解。规范做法是要么先输出第一个数字后面再输出前缀空格要么把数字存进数组最后统一输出。这类细节只有经历过一次WA才会长记性但你能从别人经验里学会就没必要自己撞墙了。// 输出每位数字空格分隔末尾无空格 int digits[10]; int cnt 0; while (n 0) { digits[cnt] n % 10; n / 10; } // 现在digits里是逆序的需要倒着输出 for (int i cnt - 1; i 0; i--) { if (i cnt - 1) printf( ); printf(%d, digits[i]); } printf(\n);2.2 数据类型选不对算得再对也白搭基础题里最容易忽略的问题是数据范围。很多人一看“输入一个整数”下意识就用int完全没注意到题目可能写的是“输入一个正整数不超过10^9”或者“输入浮点数”。10^9以内int还扛得住但一旦涉及累加、累乘结果很容易爆掉。69到73这组题里如果有数列递推、求和之类的题目中间结果大概率会超过int上限。我举个实际例子计算斐波那契数列第45项int直接溢出变成负数你还以为是算法写错了排查半天。这种坑非常隐蔽因为前几项都是对的到后面突然开始输出乱七八糟的数。所以拿到题目先看数据范围再看运算过程如果中间结果可能超过2^31-1就用long long如果涉及乘法且结果可能超过2^63-1就得考虑换思路或者用其他技巧了。2.3 循环边界与循环变量是出错的重灾区循环边界写错在69到73这组题里几乎是人人都犯过的毛病。常见的错法有三种应该循环n次写成n-1次数组下标从0开始计数但循环边界用1到n导致越界还有while循环里的条件写反一进入循环就无限循环或者直接跳过。这些问题在编译器层面不会报错只能靠你人肉debug。调这类问题我有个土办法在循环体里加一个printf打印当前循环次数和关键变量跑一次看输出就知道边界问题出在哪了。排查完记得删printf不然会因为多余输出被判PE或者WA。循环边界这个问题的本质是你对“循环不变量”的理解不够清晰——你在循环开始前、循环中、循环结束后期望哪些变量是什么状态需要心里有数这部分如果没想明白写出来的边界必然是飘的。3. 实操过程与核心环节实现3.1 数字类题目的通用拆解法东华69到73这一组里数字处理类题目出现的频率不低比如给一个正整数让你把它的每一位拆出来做运算。这类题的核心就一句话对10取余拿到最低位整除10去掉最低位循环到数变成0为止。这个“拆数”模板你只要掌握了什么求位数、求各位和、逆序输出、判断回文数全是套同一个框架的事。// 拆数模板取出正整数的每一位 // 以 n 12345 为例运行过程如下 // 第1轮digit5, n变成1234 // 第2轮digit4, n变成123 // 第3轮digit3, n变成12 // 第4轮digit2, n变成1 // 第5轮digit1, n变成0循环结束 int n 12345; while (n 0) { int digit n % 10; // 对digit做你想做的处理 n / 10; }注意一个问题这个模板对“n0”的情况是失效的因为循环直接不执行。如果题目允许输入为0你得单独判断。我在这个细节上栽过跟头题目说输入一个非负整数我默认它大于0结果0这个边界值让我直接WA。后来长记性了凡是“非负”这个字眼出现我一定要单独处理0的情况。3.2 字符统计与字符串处理的核心写法字符类的题目在69到73这组里也常出现比如统计一段字符串里数字、字母、空格各有多少个或者判断某个字符在字符串里出现的次数。C语言处理字符串和数组的区别很多新手容易绕进去——数组长度是固定的字符串却是以\0结尾的你遍历字符串的时候可能把\0也当成普通字符处理了。这里有一个关键认知在C语言里char类型的本质其实就是整数你可以直接拿字符做比较运算比如c 0 c 9判断数字字符c a c z判断小写字母。英文字母的判断还有一种写法直接用ctype.h头文件里的isdigit(c)、isalpha(c)更简洁也更不容易写错但前提是你记得引入对应头文件。// 统计字符串中的数字、字母数量 #include ctype.h char str[1000]; // 假设str已经从键盘读入 int digit_count 0; int letter_count 0; for (int i 0; str[i] ! \0; i) { if (isdigit(str[i])) digit_count; if (isalpha(str[i])) letter_count; }3.3 数组筛选与最值查找的套路“输入n个数找出最大值/最小值/所有素数/所有大于平均数的数”——这一类题目在OJ基础题中属于必考题型69到73区间应该也不会缺席。它们考察的核心能力是做“筛选”就是遍历一遍数组的同时把符合某个条件的元素摘出来。这看起来简单但有一个老生常谈的错误找最大值时max变量的初始值应该设为数组的第一个元素而不是拍脑袋设成0。为什么不能设成0因为如果数组里全是负数max0会导致你永远选不出真正的最大值。正确的初始化方式有两种一是int max a[0];先让max等于第一项然后从第2项开始比较二是把max初始化为一个非常小的数比如INT_MIN需要包含limits.h头文件。第二种方式更通用因为它不依赖于数组是否为空我个人更推荐。另一个常被忽略的细节是“位置”和“值”的关系。很多题目不仅要你输出最大值还要你输出最大值所在的位置下标。这时候你就要在更新max的同时同步更新记录位置的变量。两个变量一起维护这种思想后面学排序、学查找时也一直会用到。3.4 递推数列的实现与边界处理递推数列也是69到73这段题目里的常客比如斐波那契数列、等差数列的变体、或者自定义递推规则。递推的灵魂在于“状态转移”——后面的结果依赖前面的结果你用数组把每一次的结果存下来后面直接用而不是每次从头算。这看起来很容易理解但新手常犯的错误是数组下标越界比如第n项你存到f[n]但数组开的大小只到n一访问f[n]就越界了。数组开多大的经验法则是题目如果问你前n项你就开n1的大小甚至再大一点点留余量下标直接对应项数相比从0开始计数能少很多心智负担。另外注意递推的起点比如斐波那契的第一项、第二项分别赋什么值一定要看题目定义——有的题说第1项是0、第2项是1有的说第1项和第2项都是1这个没看清整个数列全错。// 计算斐波那契数列前n项 // 假设: f[1] 1, f[2] 1, f[k] f[k-1] f[k-2] (k 3) int f[100] {0}; // 全部初始化为0保证未赋值的元素不产生随机值 f[1] f[2] 1; for (int i 3; i n; i) { f[i] f[i-1] f[i-2]; }3.5 经典题型的完整代码实现参考到这里我完整写一道“读入正整数n输出它每一位数字的和”的代码把上面提到的拆数模板和边界处理串起来。这个函数在69到73那组题里经常作为某个小题的形式出现弄明白它你会发现在这一区间的很多题目都有它的影子。#include stdio.h int main() { int n; scanf(%d, n); // 特殊处理 n 0 的情况 if (n 0) { printf(0\n); return 0; } int sum 0; while (n 0) { sum n % 10; // 取出最低位累加到sum里 n / 10; // 去掉最低位 } printf(%d\n, sum); return 0; }这段代码的逻辑非常直观每次循环取n的最低位累加然后n缩小10倍。如果你已经想通了拆数模板还试着思考一个变体——输入正整数n计算出它总共有多少位。思路几乎一样只是每次循环里做count而不是累加。能自己推导出这个变体的写法说明你真正理解了循环和取模运算的本质而不是背模板。4. 常见问题与排查技巧实录4.1 如何系统性地排查WA错误答案WA是最磨人的因为它不像编译错误那样直接告诉你哪一行有问题。综合我这几年刷题和帮别人debug的经验WA的排查应该按一个固定的顺序来而不是东改一行西改一行碰运气。第一步检查是不是多组输入的问题。很多基础题其实会有多组测试数据如果你只处理了一组就结束第二组数据直接读不到了结果几乎必WA。如果你不确定就写个while (scanf(%d, n) ! EOF)这个写法能兼容单组和多组的情况算是万能解法。第二步检查边界值。题目有没有说n范围n1、n0、n10^9你的代码分别输出什么手动在本地跑这几个值很多问题立刻现出原形。第三步检查中间结果是否会溢出。把关键变量改成long long试试如果AC了说明是溢出问题。第四步检查输出格式。空格、换行、回车、大小写任何一个对不上就是WA。这个排查顺序看起来简单真遇到问题时能救你很多时间。最忌讳的就是“看到WA就随便改一个地方交一次”这样做事的效率极低而且会把原本正确的逻辑改坏。我当年也是交了不少“学费”才学会按顺序排查的现在直接把这个经验给你希望你少走弯路。4.2 多组输入的正确打开方式多组输入这个坎几乎每个刷OJ的人都要踩一踩。东华69到73这组题目里万一遇到“每次输入一行处理一行直到文件结束”的题你要理解的核心概念是scanf的返回值代表成功读入的参数个数。当它返回EOF也就是-1时说明输入流已经结束了。所以while (scanf(%d, n) ! EOF)这个写法本质上是在说“只要还能读到整数就继续处理”。读字符串的多组输入略有不同while (gets(str))这种写法在C11标准里其实已经不建议用了因为gets无法限制读入长度非常容易缓冲区溢出。建议用fgets(str, sizeof(str), stdin)它能限定最大读取长度。如果你要读的是单个字符还得考虑上一次输入后遗留在缓冲区的换行符通常用getchar()先吞掉那个换行。4.3 常见错误的速查对照表错误类型典型表现排查方向输出格式错误本地运行结果正确交上去WA检查末尾是否多了空格、少了换行大小写是否一致数据溢出小数据对大数据错把int换成long long检查中间运算是否爆范围循环边界错误结果整体对但个别项不对打印循环变量确认循环次数和区间开闭数组越界输出结果极其离谱检查数组下标范围确认开的大小是否够用多组输入漏读只处理了第一组数据换成while(scanf(...)!EOF)写法4.4 我常用的调试三板斧第一招是“打印中间量”。在关键步骤后加printf把变量值打到屏幕上跑完看一遍基本就定位了。查完删掉重新提交不要忘。第二招是“构造边界数据”。比如n1、n2、n100000这种极值每个都跑一遍。正常数据可能掩盖边界问题边界数据几乎一定是问题的引爆点。第三招是“小样本手算对拍”。随便挑一个小输入自己在草稿纸上算一遍期望输出再和程序输出对比。这招虽然原始但定位逻辑问题比盯着代码发呆快一万倍。这三招用熟了你的debug时间平均能缩短一半以上。更关键的是你会发现代码写的时候就越发小心了因为你开始预判哪些地方容易出错干脆从一开始就绕开那些写法。5. 实操心得与后续扩展建议东华OJ 69到73这几道题放到整个编程学习的时间线上来看其实是一个不起眼的小节点但它的价值被很多人低估了。刷这一组题表面上学的是“怎么做题”实际上学的是一种思维模式——拿到一个具体问题先拆解它的输入输出、边界条件、数据规模再选择合适的基本操作组合起来解决它。这种拆解能力才是编程和解题的核心素养你以后写任何项目、做任何开发都会用到这一套思维。我的个人建议是不要满足于“AC了就过”把这几题AC了之后再回头想想我用的方法还有没有更好的替代方案如果数据量扩大十倍我的代码还能跑吗如果把题目里的某个条件改一改我的思路还成立吗这些问题随便想一个收获都比草草刷完十道题更大。再往后你可以把目光投向东华OJ后面更进阶的题目开始出现结构体、指针、文件操作、简单排序算法、递归回溯。69到73这组题里的数组操作、循环控制、边界判断都会在后面反复出现。到那时候你回头看就会明白为什么我说“这五题值得认真对待”了。刷题是马拉松这一组题是你不会掉速的补给站慢一点不要紧关键是把每一步踩实。