聊到“3.1 OJ”这个标题我第一反应是这是一个课程讲义里的章节号大概率是某个程序设计课或者算法竞赛入门课里专门讲 Online Judge在线评测系统的那一节课。我太熟悉这个场景了——老师刚教完基本语法甩给学生一个网址说“回去注册账号以后作业都在这上面交”然后全班陷入沉默这玩意儿怎么用为什么我本地运行得好好的提交上去就是 Wrong Answer如果你也是刚接触 OJ 的新手或者已经在上面被虐过几回这篇内容就是给你写的。我会从 OJ 到底是什么、判题原理、主流平台怎么选到刷题方法论和排错心法完整过一遍。这些东西不是书面上那种干巴巴的说明而是我实际刷了几千道题、带过竞赛队之后总结出来的经验。1. OJ到底是个什么东西不只是“在线做题”那么简单1.1 一节课看懂OJ的前世今生OJ 全称 Online Judge翻译过来就是“在线评测系统”。它最早出现于 ACM 国际大学生程序设计竞赛的训练体系当时各高校为了让队员在非比赛时间也能检验自己的程序对不对就搭建了这种能自动跑程序、自动判对错的系统。后来随着计算机教育普及OJ 逐渐走进课堂成了编程作业和实验课的标准载体。用一句话概括 OJ 的功能你提交一段代码它帮你编译、运行拿你的程序输出和标准答案做比对然后告诉你这道题你做对了没有。听起来很简单但这个“自动裁判”机制解决了编程学习中一个特别关键的问题——即时反馈。你不需要等老师批改不需要找人帮你肉眼查错写完一提交几秒钟之内就知道结果。这对自学和训练来说价值巨大。很多人把 OJ 理解成“题库网站”其实不准确。题库只是它的一半另一半是评测系统。题是死的评测引擎是活的。一个程序能编译、能运行、能给出正确答案才算在 OJ 上“活”了一次。这也是 OJ 和普通练习平台最大的区别它不看你代码写得漂不漂亮只看你的程序在限定时间和资源下是否给出了正确输出。1.2 OJ解决的是三个层面的实际问题第一对初学者来说OJ 是语法练习场。刚学完 if、for、while你不知道自己到底会不会用OJ 上的入门题会逼着你把所有语法点都用一遍而且必须得出正确答案才算过关。这种“强制输出”比看十遍教材都管用。第二对竞赛选手来说OJ 是专项训练营。动态规划、图论、数论、字符串处理每一个专题都有成百上千道题等你刷。竞赛选手通过 OJ 来检验自己对某个算法的理解程度检验复杂度优化是否到位。很多国际大赛的选手就是这么练出来的没有任何捷径。第三对求职者和在校学生来说OJ 是能力测评仪。华为 OD 机考、一些互联网大厂的在线笔试用的都是 OJ 模式的判题系统。你可能不需要参加 ACM但只要走技术岗的招聘流程就绕不开这类编程评测。平时用 OJ 练出来的输入输出处理能力、调试能力和边界思维在机考现场就是实打实的竞争力。有些人觉得“OJ 不就是刷题嘛”这种理解太浅了。刷题是表象背后的编译运行机制、算法复杂度控制、严谨的边界思维才是 OJ 真正训练的能力。下面我把这层机制拆开讲。2. OJ是怎么判题的从提交到AC的完整链路2.1 一次提交的完整旅程你在 OJ 页面上贴上一段代码点击提交接下来的事情你基本看不到但这套流程值得了解因为很多“莫名其妙”的问题就出在这些环节里。第一步保存与编译。OJ 服务器收到你的代码先存成源文件然后调用对应语言的编译器。C/C 用 gcc 或 gJava 用 javacPython 直接解释执行部分平台也先编译成字节码。编译出错就直接返回 Compile Error也就是 CE。这个阶段常见问题包括头文件写错、主函数签名不对比如把 int main 写成 void main、缺少必要的库引用。本地能过但提交 CE九成是编译器版本或标准差异导致的比如有些学校的老 OJ 还在用 C98 标准你用了 C11 的语法就会编译失败。第二步沙箱运行。编译通过后OJ 会把你的程序放进一个受限的沙箱环境里运行。什么是沙箱你可以把它理解成一个“封闭考场”——程序在里面没有网络没有多余的文件读写权限只能读指定的输入文件只能往标准输出打印结果而且有时间限制和内存限制。这样设计是为了防止恶意代码搞崩服务器也确保所有提交都在公平的环境下评测。第三步输入灌入。OJ 后台有多组测试数据你的程序运行后评测系统会把第一组输入数据灌给你记录你的程序输出再和标准输出逐字节比对完全一致才算通过这一组数据。注意我这里说的是“逐字节比对”空格、换行、大小写都算数。经常有人问“我输出明明是对的为什么 WA”十有八九是多了个空格或者少打了个换行。第四步结果汇总。多组数据全部通过就是 AcceptedAC任何一组出了问题就返回对应的错误提示。OJ 判题的底层逻辑就这么简单但正是这种“简单粗暴”的比对方式让无数人栽了跟头。你本地跑得再欢只要有一次输出带了个多余空格OJ 就是不认。2.2 判题结果的行话全解读每个刷题的人都得懂这套“黑话”不然看到 WA、TLE 这些缩写会一头雾水。我把最常见的几种结果整理成了一张速查表缩写全称含义常见原因ACAccepted通过无庆祝就完事了WAWrong Answer答案错误算法逻辑不对、边界没考虑、输出格式不符TLETime Limit Exceeded超时算法复杂度过高、死循环、递归没写出口MLEMemory Limit Exceeded内存超限数组开太大、递归层数爆栈、容器无限制扩容RERuntime Error运行时错误数组越界、空指针、除零、递归栈溢出CECompile Error编译错误语法错误、头文件缺失、编译器标准不符PEPresentation Error格式错误答案内容对但空格、空行位置不对OLEOutput Limit Exceeded输出超限程序输出大量调试信息或死循环输出这里面最折磨人的是 PE。它意味着你的程序输出内容本身是对的就是格式差了一点多打了个空格或者空行。老一点的 OJ 会严格区分 PE 和 WA现在很多新平台已经直接归到 WA 里去了所以你可能连“解释的机会”都没有。这也是我一直强调“先看输出格式再想算法”的原因。2.3 为什么“本地能跑OJ上过不了”这是新手遇见的第一个玄学问题。明明在自己的 IDE 里运行得好好的复制到 OJ 提交就 WA、RE甚至 CE。我总结了几条最常见的原因编译器差异是最隐蔽的。学校机房如果是老旧的 Visual C 6.0标准是 C98但你在本地用的新版 GCC 支持 C17。一些新语法在老编译器下直接编译失败。更坑的是未定义行为——比如数组越界在本地可能碰巧没报错在 OJ 的沙箱环境下就 RE。这类问题没有太多好的办法只能靠规范编程来规避不要依赖未定义行为数组开大一点初始化所有变量。多组输入处理不当也很常见。很多 OJ 题要求读入到 EOF文件结束符也就是你并不知道有多少组数据必须持续读直到读不到为止。新手如果只按一组数据处理那第一组数据过了、第二组就 WA 了。这块我在第 4 部分给出标准模板。局部变量的栈空间也要注意。在函数内部开一个 int a[1000000]在本地可能因为栈空间大而侥幸运行在 OJ 的沙箱里直接爆栈变 RE。正确做法是开成全局变量或者用 new 动态分配或者用 vector。3. 平台怎么选六类OJ各有各的脾气3.1 国内外主流OJ平台对比热词里的“杭电 OJ”“华为 OJ”“江南 OJ”这些关键词说明很多人正在寻找具体的 OJ 平台。我按自己的使用经验把常见的几类平台做一个对比方便你选型平台特点适合人群语言支持杭电OJHDUACM经典题库历史悠久题量大题目质量高国内高校ACM入门首选C/C/Java等北大OJPOJ国内老牌OJ题目经典部分题老了点考研、ACM老玩家C/C/Java/Pascal洛谷中文社区极活跃题解丰富按难度分级有题单从入门到竞赛各阶段都很友好C/C/Java/Python等Codeforces全球顶级算法平台定期周赛题目新颖想进阶竞赛水平的必刷C/C/Java/Python等AtCoder日系平台题解质量高对新手更友好喜欢有梯度场次的新手和进阶者全语言牛客网国内面试刷题平台企业真题多找工作的求职党全语言学校自建OJ按课程要求定制题量不大但针对性强在校生应付作业必用以C/C为主这里特别说一下“华为 OJ”。它不是一个公开的刷题平台而是华为招聘机考时使用的在线评测系统现在主要开放给通过简历筛选的候选人在牛客或华为自己的平台进行。题目风格偏经典算法例如二叉树、排序、动态规划、字符串处理难度中上限定时间内完成。想准备的话可以直接在牛客上搜“华为机考”题库来练跟竞赛刷题不太一样的是华为机考更贴近实际业务场景下的算法问题不追求极致的复杂度优化更看重完整度和工程规范性。3.2 热词里的“东方博宜答案”“杭电1002”都在搜什么热搜词里出现了大量“东方博宜 oj 答案1065”“东方博宜 oj 答案1168”这样的组合。东方博宜是一个面向中小学和初学者的OJ平台题目偏基础Python 和 C 的入门题都有很多学校的信息课会用它布置作业。搜“答案”的同学多半是作业卡住了。这个问题我后面单独说先给一个态度OJ 平台确实能搜到答案但直接抄没有任何意义因为 OJ 判题只输出一个正确与否如果你连错误原因都不清楚抄完下次换个数你还是不会。杭电 OJ 1002 题是一道经典题A B Problem II也就是大数相加。这道题本身逻辑很简单但它考的是高精度处理——用 int 装不下两个很大的数相加的结果必须用字符串模拟加法。1002 之所以经典是因为它是很多新手第一次接触“数据类型装不下”这个概念的启蒙题。如果你正在做这道题我的建议是先想清楚怎么逐位加、怎么处理进位再用数组或字符串实现别一上来就翻题解。杭电 1020 是另一类经典题字符串编码压缩比如把连续重复的字符转换成“字符次数”的形式。这类题不难但很考对字符串边界条件的处理——最后一组字符的收尾一不小心就漏掉。杭电 1096 则是典型的“多组数据 格式控制”题练的就是第 4 部分要讲的输入输出模板。至于“西北农林科技大学 C OJ 智能指针”这是学校自建的课程 OJ 里的题目涉及 C 的智能指针unique_ptr、shared_ptr知识。这类题重点考察你对动态内存管理和所有权语义的理解做题时先别急着写代码把智能指针的三种类型和适用场景理一遍效率会高很多。3.3 学校OJ和公开OJ的取舍我见过不少同学学校 OJ 的作业都要拖到截止时间才动手更别说自己去外网刷题了。但说实话如果只靠学校 OJ 那点题量编程能力撑死到及格线。学校自建 OJ 通常是根据课程进度出的题题量少、专题覆盖不全、数据强度也可能不高。它的核心角色是“教学辅助”保证你跟上课程进度。真正拉开差距的是自己额外在公开 OJ 上的训练。我建议的路线是日常作业用学校 OJ 保证不挂科课余时间在洛谷或杭电 OJ 上自己定题单刷题。先做一些水题建立信心然后按专题推进。公开 OJ 的好处是题量大、题解多、讨论区活跃你能看到别人的思路和代码风格这是学校 OJ 提供不了的。4. 刷题方法与路径规划从青铜到王者的进阶路线4.1 新手三层进阶法我见过太多新手一上来就找最难的题刷然后被打击得体无完肤直接弃坑。刷题这件事是有节奏的我建议你严格按三个层次推进第一层是语言基础题目标就是熟悉语法和输入输出。找那种“输入两个数输出它们的和”级别的题刷上二三十道。这一阶段的核心不是算法而是要练到“看到任何输入输出格式都能不假思索地写出对应代码”。这一步很多人不耐烦但请相信我后面你会感谢自己的——因为很多 WA 就是栽在输入输出格式上。第二层是经典入门算法题包括枚举、模拟、排序、贪心、二分查找。这些算法是后面一切复杂题目的地基。这阶段不用追求题量每一道题都要把思路理清楚为什么用这个算法、时间复杂度多少、边界在哪里自己能在纸上画出来才算过。建议一个专题一个专题地刷比如连续一周只做二分查找直到看到最大最小值就知道要二分。第三层是数据结构与竞赛专题也就是栈、队列、链表、二叉树、并查集、图论、动态规划。刷到这里你已经不是新手了。这阶段的正确打开方式是“先学习理论再刷题验证”。比如学了动态规划的状态转移方程马上去找 DP 的经典题做不出来就回头看书不要硬刷。4.2 三个必须背下来的输入输出模板下面这几个模板是我认为刷 OJ 之前必须烂熟于心的直接决定你能不能在这个平台上活下去。我分别给 C 和 Python 的版本因为这两个是 OJ 平台上最常见的语言。单组输入C 语言int a, b; scanf(%d %d, a, b); printf(%d\n, a b);多组输入直到 EOFC 语言int a, b; while (scanf(%d %d, a, b) ! EOF) { printf(%d\n, a b); }多组输入直到 0 0 结束C 语言int a, b; while (scanf(%d %d, a, b) 2) { if (a 0 b 0) break; printf(%d\n, a b); }Python 多组输入直到 EOFimport sys for line in sys.stdin: a, b map(int, line.split()) print(a b)注意一个特别容易踩的细节输出一组结果后必须换行。有些题目还要求两组输出之间加一个空行例如“cases 之间有一个空行”这需要额外控制变量来判断当前是不是第一组输出。题目的输出描述会写得很清楚做题前一定要看懂。4.3 拿到一道题的正确审题顺序很多 WA 不是你不会做而是你根本没看清题目。我给自己定的审题顺序你可以直接抄第一先看数据范围。数据范围决定算法选型。如果 n 最大是一万O(n^2) 一般可以如果是一百万O(n^2) 基本必 TLE必须想 O(n log n) 或者 O(n) 的解法。这一条判断错了后面全是无用功。第二再看输入输出格式。有几组数据什么时候结束输出有没有空格、换行、空行的要求这些细节我会在看题的第二分钟确认而不是写了一半才发现漏了。第三想边界条件。输入是 0 怎么办是负数怎么办是空字符串怎么办数组里只有一个元素怎么办在纸上把这些情况过一遍再开始写代码。第四估算复杂度。OJ 的时限通常是 1 秒到 2 秒以 C 单线程来说1 秒大约能跑 1e8 次简单运算Python 的话大约 1e6 甚至更少。如果你的算法运算量明显超过这个数趁早换思路。5. 从WA到AC的排错心法5.1 一个百试百灵的WA排查清单在 OJ 上卡题太正常了我讲讲我自己的排错流程。当出现 WA 时按这个清单一项一项查先查输出格式。空格、换行、大小写、空行这是最低级也最高频的错误。我见过有人把 Yes 和 YES 搞混然后白白交了三次。再查边界条件。0、负数、最大值、空输入有没有处理很多 WA 就是被一组你没考虑到的边界数据卡掉的。再查数组大小。开小了是 RE开得不够大是越界导致各种诡异结果。我有个习惯题目说 n 10000我至少开成 10005多留一点余量。如果涉及字符串处理记得字符数组要多开一位给\0。再查循环变量类型。比如在杭电 1002 大数相加里如果你用 int 存高精度运算的下标数据一大就溢出直接 WA 或者 RE。凡是索引比较大的场景统一用 long long 或者 size_t别偷懒。最后查逻辑。如果是算法题确认你想的思路本身对不对可以拿暴力解法做交叉验证。这个方法下面详细说。5.2 调试三板斧打印法、边界样例法、对拍法第一招打印法。在关键位置插入 printf/cout 或 print输出中间变量。这招土但在 OJ 环境下是最可靠的。你提交之前可以无限次在本地打印调试确定所有分支执行正确后再删掉调试输出。注意提交前一定删干净否则可能 OLE 或者格式错误。第二招边界样例法。构造极端输入来测试你的程序。比如求最大公约数的题思考两个数相等时输出了什么其中一个数为 1 时呢含 0 时呢这些边界样例往往就是 OJ 判题数据里的隐藏炸弹。第三招对拍法。这是进阶技巧专门对付“思路想错但小数据全过”的情况。写一个绝对正确但复杂度高的暴力程序再写一个你认为效率高的优化程序然后写一个随机数据生成器把两个程序的输出逐行比对。一旦某组数据两边输出不同就说明你的优化代码存在 bug。网上有很多对拍脚本模板比如 Linux 下用 bash 循环跑 Python 脚本造数据我强烈建议学会这一招它能帮你省下大量卡题时间。5.3 卡题不丢人别死磕也别抄答案我自己的经历里卡题超过两个小时就没有继续硬着头皮想的意义了。这时候正确做法是先放一放看看题解思路注意只看思路不要看代码然后合上题解自己重写一遍。如果重写不出来再看代码看懂了以后关上编辑器默写一遍。这是最高效的成长路径。千万不要直接复制提交别人的代码理由很简单OJ 的题目你抄来的 AC 只是数字上的通过你自己还是不会。很多有经验的 OJ 用户一看代码风格和提交记录就知道哪些人是靠抄的这不是竞赛训练的目的。但如果你真的卡了很久、实在想不出来可以参考讨论区的思路帖然后理解后自己写。还有一个心态问题很重要OJ 上通过率 5% 的题你通宵硬刷到 3 点才过不丢人那是勋章。同理一道题你提交了 13 次全 WA再试一次可能就过了。编程能力不是线性积累的很多突破是在崩溃的边缘出现的。我个人在实际操作中的一个习惯是每道 AC 的题都会在题解区扫一眼看看有没有比我的解法更优的思路。很多时候你以为的完美答案在别人那里可能有一段差距。算法这条路重要的是始终保持开放。如果你现在刚刚在某个 OJ 上注册好账号面对第一道题不知所措那就先从最基础的 AB 开始吧——背熟输入输出模板把本章的排错清单存下来遇到 WA 不要慌一个一个问题排除。等你能连续提交十道题全部一遍过的时候你会明显感觉到自己已经在编程这条路上走进来了。