我翻了近一周的提交记录,发现很多人在OJ 71、72、73这三道题上反复进出,有人第一题AC后连着卡第二题三天,有人三道题全绿却在讨论区里问“为什么我的代码本地跑得好好的,一交就错”。这场景太熟悉了,每个刷OJ的人都经历过这种“生死时刻”。
这三道题我前前后后做了三遍,第一遍是纯粹为了过题,第二遍是回头整理思路,第三遍是拿来做教学案例。做完了有个很深的感触:OJ 71、72、73放在一起不是偶然,它们分别对应了程序设计基础里三个最容易踩坑的能力点——数学建模与优化、线性结构的选择与维护、递归与二叉树的边界控制。而且这三道题对输入输出格式的要求都非常刁钻,正好能把“本地能跑”和“OJ能过”之间的差距暴露得明明白白。
这篇文章就把我对这三道题的拆解思路、AC代码、踩坑记录和对拍排查方法完整分享出来,给正在跟这三道题较劲的人一个参考。不管你是刚接触OJ的新手,还是已经刷了不少题的“老油条”,里面应该都有值得看一眼的东西。
1. OJ 71、72、73的题目定位与考点分析
1.1 三道题目的真实难度层级
先说结论:OJ 71和72在难度上属于“看着容易、做起来恶心”的类型,OJ 73则是“思路清晰、代码写起来全是雷”的类型。
我反复做了几遍之后,给这三道题做了一个比较客观的定位:
| 题号 | 核心考察点 | 常见错误类型 | 建议完成时间 |
|---|---|---|---|
| OJ 71 | 模拟 + 数学优化 | 超时、边界值错误 | 30-60分钟 |
| OJ 72 | 线性表操作 + 数据结构选择 | 思路复杂化、逻辑遗漏 | 40-90分钟 |
| OJ 73 | 二叉树遍历与重建 | 递归终止条件错误、数组越界 | 60-120分钟 |
很多人有个误区,觉得OJ编号越靠后越难,但实际上71、72、73这三道题并不是线性递进的。71卡的是你有没有“把模拟过程抽象成数学规律”的意识,72卡的是你对数据结构本质的理解,73卡的是递归边界和细节控制。
这三道题放在一起,恰好构成了一个完整的PKU式训练链:描述很简短,数据范围很大,解法很单一,坑点很隐蔽。
1.2 为什么很多人会在这三道题上反复崩溃
我观察了一下讨论区的提问频率,发现几个高度集中的问题类型。
第一类是OJ 71的“超时地狱”。题目描述看起来就是一次简单的循环模拟,很多人按照直觉写了双重循环,样例能过,一提交就TLE。为什么?因为题目给的N范围是10的5次方甚至10的6次方,双重循环是10的10次方量级,在现代OJ上两秒时限绝对跑不完。
第二类是OJ 72的“过度设计”。有些人一看到题目就条件反射地想到线段树、树状数组、平衡树,写了两百行代码,结果逻辑漏洞百出。实际上这道题用最基础的栈或者双端队列就能解决,关键是你要能识别出“这个操作序列具有后进先出的特性”还是“先进先出的特性”。
第三类是OJ 73的“递归失控”。用递归建树或者遍历的时候,终止条件写错一个等号,递归深度就多一层,小数据看不出来,大数据直接栈溢出,或者出现野指针。
这三类问题实际上反映的是同一个本质:刷OJ的人太关注“怎么写代码”,而忽略了“先想清楚再写代码”。我后面会按题号把这三道题的完整拆解过程写出来,你会发现大部分时间应该花在纸上,而不是键盘上。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. OJ 71的数学建模:从暴力模拟到公式化简
2.1 题面还原与直觉陷阱
OJ 71的题面我给一个大致描述:给定一个长度为N的数组,要求在某个规则下重复执行某种变换,直到数组满足特定条件,输出变换次数。
为了不直接泄题,我用一个等价的经典模型来说明:
有N个人围成一圈,从第1个人开始报数,报到M的人出圈,然后从下一个人重新开始报数。问最后一个出圈的人是谁。
这就是约瑟夫问题的变体。很多人的第一反应是“模拟一个循环链表”,然后写一个while循环不断遍历,标记出圈的人。这种做法在N小于1000的时候完全没问题,但当N达到100000甚至更大时,链表模拟的时间复杂度是O(N×M),直接爆炸。
而OJ 7
