ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

东华复试OJ第16-18天复盘:三天九题夯实算法与数据结构

东华复试OJ第16-18天复盘:三天九题夯实算法与数据结构 每天早上八点打开东华复试OJ三道题一坐就是一上午——这是我在准备复试那阵子给自己定的规矩。第16天到第18天刚好是个分水岭前半个月还在磨输入输出、数组、循环这些基本功从第16天开始题目明显拐弯变多了排序开始带多关键字字符串开始藏陷阱模拟题开始抠边界条件。这篇复盘就是这三天的完整记录九道题按天拆开每道题说清楚思路、关键代码、踩过的坑。如果你也在准备东华或者类似学校的复试机试或者你正在OJ刷题但总觉得刷了没效果这份复盘应该能帮你少走不少弯路。1. 每日三题是怎么安排的1.1 为什么是三题而不是十题准备复试初期我也走过“一天刷十题”的弯路。结果是什么题目是刷了但都是浅尝辄止看着题目有点思路写一半卡住看一眼题解恍然大悟然后下一题。这种刷法有个很大的问题——缺乏深度的AC练习。OJ上的AC不是“我大概会了”而是代码写完、调试通过、边界覆盖、提交一次过整个过程里你能清晰看到自己哪里想漏了、哪里写错了。一道题从读题到AC认真做至少四十分钟一天十题根本不现实硬刷只会变成“浏览题解”。所以我把目标定成每天三题。三题刚好能覆盖两到三个知识点做完之后还有余力写复盘、整理模板。第16到第18天我的节奏已经很固定上午九点到十一点做题下午花半小时复盘晚上把当天卡住的点整理到错题本里。三题不多但每一题都扎扎实实过了一遍完整闭环。1.2 这三天的题型清单既然说了是复盘先把这三天九道题的清单摆出来。东华复试OJ的题目风格整体偏基础但很爱考“模拟 边界条件”字符串、结构体排序、简单数学是大头。下面是我这三天的题单按每天三题整理天数题目编号题型核心考点做题状态Day 1616-1日期计算闰年判断、月份天数数组二次ACDay 1616-2英文单词统计字符串处理、状态标志一次ACDay 1616-3成绩排序结构体、sort的cmp二次ACDay 1717-1大整数加法高精度、进位处理三次ACDay 1717-2约瑟夫环数组模拟、取模运算三次ACDay 1717-3进制转换除法倒序、字母映射一次ACDay 1818-1区间素数统计素数筛、前缀和思想二次ACDay 1818-2蛇形矩阵方向数组、边界判断三次ACDay 1818-3括号匹配栈、字符串遍历一次AC题目本身是我按东华复试常见风格重构的示例题不是官方题库编号。东华复试OJ的真实题库不会公开与其到处找所谓的“原题”不如踏踏实实把这类基础题吃透考场上变来变去都跑不出这个范围。1.3 刷题时间段的分配具体时间上我一般把两个小时的做题时间切成“30分钟 50分钟 40分钟”三段。第一题通常是热身题难度低控制在半小时内性价比最高第二题是当天的核心题可能涉及结构体排序或者高精度留足五十分钟第三题我习惯放一道需要动点脑子的模拟题四十分钟是上限超时就直接看题解标星第二天重做。大家可以根据自己的复试时间调整但核心思路是一样的不要把三个小时平均分给三题要把主要精力留给当天的“硬骨头”。2. 第16~18天九题复盘2.1 Day 16模拟和字符串16-1 日期计算题面大意给一个年月日输出这是当年的第几天以及这天是星期几。日期题是OJ最爱考的模拟题没有之一。拆开看就两个点月份天数数组 闰年判断。我一开始写月份天数数组写成了int dayTab[12] {31,28,31,30,31,30,31,31,30,31,30,31}这没问题问题出在for循环从0还是从1开始。我习惯下标从0开始所以for (int i 0; i month - 1; i)累加天数最后再加上day。这个“月减一”的细节第一次写的时候漏了导致1月15日算出来多了31天。闰年判断口诀能被4整除但不能被100整除或者能被400整除。写成代码bool isLeap(int y) { return (y % 4 0 y % 100 ! 0) || (y % 400 0); }注意闰年只影响二月的天数也就是累加时如果i 1且是闰年要加29而不是28。这个常识不难但考试一紧张很容易忘。星期几的计算更麻烦我的做法是先把一个已知的基准日期比如2024年1月1日是星期一拿来做参照先算从基准日期到目标日期的天数差再模7。这里最容易被坑的是跨年计算所以最好直接封装一个“从公元1年1月1日到某天的总天数”的函数思路清晰还不容易错。16-2 英文单词统计题面大意输入一行英文句子统计单词个数忽略连续空格和首尾空格。这题考的是状态标志不需要split直接遍历。核心思路用一个布尔变量inWord表示当前是否处于单词中碰到空格就inWord false碰到非空格且inWord false就计数加一并把inWord true。坏就坏在输入。OJ上很多这类题目字符串一行里可能有空格不能直接用cin str因为cin遇到空格就停了。我一开始用char s[105]; gets(s);本地没问题提交到OJ直接编译不过新版OJ的编译器早把gets废弃了。老老实实用Cstring s; getline(cin, s);这里还有个坑如果前面用过cin n后面再getline(cin, s)会吃掉一个换行符导致读进来的字符串是空串。解决办法是读完整数之后多加一个getline(cin, tmp)把换行消费掉。这个问题在OJ里非常常见后面第三章我会专门说。16-3 成绩排序题面大意输入若干学生信息包括学号、姓名、成绩先按成绩降序成绩相同按学号升序。结构体排序是复试必考基本属于白给题。但白给题最容易掉以轻心。我用的是C的sort 自定义cmp函数struct Student { string id; string name; int score; }; bool cmp(const Student a, const Student b) { if (a.score ! b.score) return a.score b.score; return a.id b.id; }这里最关键的教训是cmp里必须把“相等的情况”写清楚否则行为未定义。我第一版只写了return a.score b.score成绩相同的时候两个学生谁前谁后由sort内部决定这不叫错但不稳定。OJ判重样例有时候会在隐含条件里卡这种不稳定的排序而且“成绩相同按学号升序”是题目明确要求的不写就WA。多组输入也是个问题。每组数据之前存学生的vector必须清空否则上一组数据会叠加进来答案莫名其妙多出一堆人。2.2 Day 17经典数据结构和数学17-1 大整数加法题面大意输入两个可能超过long long范围的正整数最多几百位输出它们的和。高精度加法东华复试的常客很多年都考过类似题目。这个题我第一次没AC原因很丢人——我把两个数字串按从高位到低位存进了vector然后从左往右加进位全都往右跑了。正确的姿势应该是倒序存储把个位放在vector[0]从低到高逐位相加最后如果有进位再push_back。我后来封装的模板长这样vectorint add(vectorint a, vectorint b) { if (a.size() b.size()) return add(b, a); vectorint res; int carry 0; for (int i 0; i a.size(); i) { int t a[i] carry; if (i b.size()) t b[i]; res.push_back(t % 10); carry t / 10; } if (carry) res.push_back(carry); return res; }坑有三个。第一短的数补0别在循环里反复判断越界写成两段循环直接在一个循环里用if (i b.size())处理最干净。第二结果可能等于0单独处理。第三输入可能带前导0比如“000123”我一开始没预处理结果输出带上了一堆没用的0交上去WA了半天。17-2 约瑟夫环题面大意n个人围成一圈从第一个人开始报数报到m的人出列输出出列顺序或者求最后剩下的人。这个题经典到不能再经典了。复试考它一般n比较小直接模拟就行。我用vector模拟每次出列下标i (i m - 1) % v.size()然后v.erase(v.begin() i)。这里最容易错的就是这个m - 1因为报数从当前人开始第一个人相当于占了一个位置所以到出列的人偏移量是m - 1而不是m。还有一点erase之后vector长度变了索引要留在原位置不要额外加一因为下一次报数要从出列位置的下一个人开始而erase之后这个人的位置正好是新的v[i]。这个逻辑我第一次写懵了debug花了快四十分钟。很多网上的解法用链表或者队列但复试场景下vector模拟完全够用因为数据量小写起来快不容易出错。17-3 进制转换题面大意输入十进制数n和进制k输出k进制表示。k不超过16。这个题其实是白给题核心就一个循环不断取余、整除、把余数映射到字符。我一次AC的原因是我把坑都提前规避了string s; if (n 0) s 0; while (n 0) { int r n % k; if (r 10) s (0 r); else s (A r - 10); n / k; } reverse(s.begin(), s.end());最大的坑就是n 0要特判否则输出空字符串WA到怀疑人生。还有一个容易被忽略的点十进制数可能是负数。题目如果没说默认非负如果说了可能为负可以先记录符号然后对绝对值转换最后拼上负号。2.3 Day 18筛法、矩阵和栈18-1 区间素数统计题面大意给定区间 [a, b]统计区间内素数个数。如果只在复试前刷题不多很容易一上来就写“判断素数”函数每个数都去试除这样写虽然能过小数据但OJ常规会卡时间。我直接用欧拉筛先把范围内所有素数筛出来再统计。欧拉筛模板在第四章会给出。但真正让我WA的不是筛法本身而是区间的两端。题目给的a和b哪个大哪个小不一定我第一版直接默认ab结果ab的时候输出负数明显不对。后来加了if (a b) swap(a, b)。还有一个边界1不是素数0也不是筛法初始化要把isPrime[0] isPrime[1] false别漏了。18-2 蛇形矩阵题面大意输入n输出n阶蛇形矩阵或螺旋矩阵数字从1开始顺时针填满。这个题我卡了三次AC全死在边界条件上。后来我总结出一个通用写法方向数组 四个边界变量。int dx[4] {0, 1, 0, -1}; int dy[4] {1, 0, -1, 0}; int x 0, y 0, dir 0; for (int i 1; i n * n; i) { matrix[x][y] i; int nx x dx[dir]; int ny y dy[dir]; if (nx 0 || nx n || ny 0 || ny n || matrix[nx][ny] ! 0) { dir (dir 1) % 4; nx x dx[dir]; ny y dy[dir]; } x nx; y ny; }这个套路的核心是“先看下一步能不能走不能走就拐弯”。判断条件是matrix[nx][ny] ! 0也就是已经填过的格子不能重复走。这个条件比单独维护上下左右四个边界要省心不容易出逻辑漏洞。唯一要注意的是如果n * n很大matrix必须开在全局变量否则局部数组太大直接爆栈。18-3 括号匹配题面大意给一个只包含()、[]、{}的字符串判断括号是否合法匹配。又一道栈的经典题。思路不复杂但OJ上的变体往往是混着普通字符一起给比如a(b[c])d这种。正确的做法是忽略非括号字符只把括号丢进逻辑里。我当时第一版直接判断s[i]是不是括号之一是括号才处理这样最稳。栈的几个细节右括号到来时如果栈空直接不匹配左括号压栈时压什么建议压对应的右括号这样匹配的时候直接比较当前字符和栈顶逻辑最顺手stackchar st; for (char c : s) { if (c () st.push()); else if (c [) st.push(]); else if (c {) st.push(}); else if (c ) || c ] || c }) { if (st.empty() || st.top() ! c) { ok false; break; } st.pop(); } } if (ok st.empty()) cout YES endl; else cout NO endl;最后别忘了判断st.empty()——如果遍历完了栈里还有左括号说明有括号没闭合照样不匹配。3. 这三天反复踩的三个坑3.1 数组越界日期和矩阵的边界第16天的日期计算和第18天的蛇形矩阵本质是同一个坑边界条件没写全。日期计算里我漏了二月的闰年判断矩阵填数里我漏了“下一个位置是否越界”的检查。这两个错误在本地跑样例的时候往往测不出来因为样例数据刚好不触发边界提交到OJ上就翻车。我的经验是写模拟题的时候把每一个“下一步操作”都问一遍会不会越界、会不会重复、会不会取到空。日期题就画一张月份天数表矩阵题就画一张小规模手写走格子。这比提交后看WA再猜要高效得多。3.2 排序cmp的“相等陷阱”排序题的cmp函数有个隐性规则当两个元素相等时cmp必须返回false。如果你只写了return a.score b.score在成绩相等时a和b谁排在前面对cmp来说都是true这会破坏sort对严格弱序的要求。网上很多说法是“编译不报错但运行可能崩”我的实际体验是大部分时候不崩但结果不稳定而且OJ的测试数据一旦针对这个卡你就是WA。正确的写法就是把所有并列条件都写进去一直写到某条主键彻底区分两个对象为止。如果再极端一点连学号都一样那就再加一个姓名保证任意两个元素都可以被严格比较。3.3 多组输入听说你又被EOF卡住了OJ里有大量“多组输入”题目三种常见形态以EOF结束、以0结束、以空行结束。第17天的大数加法第18天的素数统计都是多组输入我在这个坑里至少浪费过半小时。以EOF结束的用while (cin n)以0结束的可以用while (cin n n ! 0)。但最阴间的其实是“读完整数后要读带空格的字符串”这种组合。cin n会留下一个换行符在缓冲区接着getline(cin, s)读到的就是空串。处理方式有两个要么在主逻辑之前加一个cin.ignore()要么干脆把所有输入都当作字符串一遍getline读进来再自己解析。复试机试时间有限我更推荐后者因为字符串解析对输入格式的要求更宽松不容易被缓冲区的残留换行坑到。4. 我自己沉淀的四个模板4.1 多组输入模板我把多组输入分成三件套// 情况1: EOF结束 while (scanf(%d, n) ! EOF) { } // 情况2: 读到0结束 while (cin n n ! 0) { } // 情况3: 按行读行内含空格 string line; while (getline(cin, line)) { if (line.empty()) continue; // 跳过空行 }不要每一次都现场想背下来。复试的时候时间紧迫输入框架稳定了主要精力才能留在算法上。4.2 结构体排序模板结构体排序是东华复试OJ的高频题。模板核心是cmp函数多关键字一定要写全struct Node { int id; int score; string name; }; bool cmp(const Node a, const Node b) { if (a.score ! b.score) return a.score b.score; if (a.id ! b.id) return a.id b.id; return a.name b.name; }如果题目要求“按输入顺序输出成绩相同的”那就是需要稳定性直接改用stable_sort不要和sort死磕。4.3 高精度加法模板前面已经给了add函数这里补一个从字符串到vector的初始化和输出vectorint str2vec(string s) { vectorint res; for (int i s.size() - 1; i 0; i--) { res.push_back(s[i] - 0); } return res; } void printVec(vectorint v) { for (int i v.size() - 1; i 0; i--) cout v[i]; cout endl; }高精度减法、乘法都是在加法基础上扩展先把加法模板背熟考场上至少能拿下一道大题。4.4 素数筛模板区间素数的标准解法是欧拉筛也叫线性筛。它的意思是每个合数只会被它的最小质因子筛掉一次时间复杂度O(n)比埃氏筛稳定。我直接给模板vectorint primes; vectorbool isPrime(n 1, true); isPrime[0] isPrime[1] false; for (int i 2; i n; i) { if (isPrime[i]) primes.push_back(i); for (int j 0; j primes.size() i * primes[j] n; j) { isPrime[i * primes[j]] false; if (i % primes[j] 0) break; } }这个模板里if (i % primes[j] 0) break;是灵魂没有这一句它会退化成普通筛法复杂度下不来。另外i * primes[j]在极端情况下可能溢出int如果n接近2亿建议把i和primes[j]转成long long再乘。5. 复盘到底要记什么5.1 我的复盘模板长这样刷题不复盘等于白刷。我每天刷完三题会花二十分钟填一张固定模板的表格存在本地Markdown文件里。格式大概是这样项目内容日期2025-xx-xx题号16-1题型日期模拟AC次数2卡点描述二月天数没算闰年一句话解法月份天数数组 闰年特判基准日期算星期是否需要二刷是“一句话解法”是复盘里最重要的部分。写这一句话的过程实际上是在逼自己提炼题目的核心套路。能把解法压缩成一句话说明真的理解了这个题而不是只会照着题解敲代码。5.2 哪些题值得二刷三刷我的标准很简单AC次数超过两次的题、卡住超过一小时的题、包含新模板的题都进二刷名单。二刷不是重新做完整套代码而是只看题面在纸上或者编辑器里写出核心代码和一句话解法然后和标准解法对一下。如果思路对上了直接过如果思路对不上重新做一遍。第17天的大数加法、第18天的蛇形矩阵都属于这三天里必须二刷的。因为它们不光考知识点还考代码实现的稳定性。复试现场是不允许你反复试错的一次AC的能力就是靠二刷刷出来的。5.3 复盘记录的是解法而不是感动刚写复盘时我容易写很多“今天好难”“差点没做出来”这种情绪化的话后来发现完全没用。复盘不是日记是给未来的自己看的技术索引。记录重点应当是这个题用了什么数据结构、哪种算法套路、哪些边界条件容易漏。情绪随笔可以写在自己朋友圈里复盘文档里只需要干货。6. 给同样准备复试机试的同学的建议6.1 平台怎么选东华复试用的OJ是学校自己的系统平时刷题完全可以去其他OJ练手。杭电OJ题目全、难度梯度大适合打基础郑轻OJ和东方博宜OJ有很多适合考研复试的基础题可以拿来当模拟训练华为OJ更偏向笔试风格题目细节描述多适合后期练读题和边界处理。我的建议是选一个平台主刷不要频繁换。每个OJ的输入输出风格略有差异但核心能力是一致的把题目转换成代码的能力。主刷平台不换是为了把手感和模板稳定性练出来。另外在线判题系统的原理大同小异都是读标准输入、跑程序、比对标准输出。复试前最好多在自己平时用的编译器里用“标准输入输出”方式跑题不要依赖IDE的交互输入否则考试时可能不习惯黑盒判题模式。6.2 时间怎么控复试机试通常时间紧张一道题从读题到AC控制在30到40分钟比较合理。超过了就直接看题解标星二刷。很多同学怕看题解显得自己菜其实恰恰相反备考阶段效率最高的人都是敢看题解并且能把它消化成自己的模板的人。真正的能力体现在第二天早上能不能独立AC同一道题而不是当天死磕多久。做题顺序上先扫一遍所有题把最简单的题先AC掉保住基本分再啃中等题最后尝试难题。这个策略在OJ上尤其重要因为OJ只看AC数不看过程分一题没AC就是零分先拿能拿的分再冲刺高分。6.3 考前一周做什么考前一周不要再刷新题了。把之前所有复盘文档翻出来逐个过一遍“一句话解法”然后在本地把四个高频模板默写一遍多组输入、结构体排序、高精度加法、素数筛。如果还有时间就把标记了“二刷”的题重新做一次。真正考试时你会发现你调用模板的速度决定了你交卷的速度。我个人印象最深的一点是复试机试考的不是谁刷的题多而是谁在限时环境里写代码更稳。稳从哪里来从一遍遍AC的肌肉记忆中来从复盘文档里一条条整理好的边界条件中来从“看到题型立刻想到模板”的条件反射中来。这三天九题做下来我第一次感觉到自己不再是拿题就懵的状态了。每个题拿过来脑子里会自动冒出一个大致的结构要不要用栈、要不要排序、要不要处理边界、输入是行还是数字。这种手感不是天生的是几百道OJ题和几十次复盘一点点喂出来的。第16天到第18天只是这条路上一个小小的台阶但这个台阶迈过去之后后面的路确实好走多了。
返回列表