新学期又到了我带过几届大一程序设计课程每到教材“第三章第一节”这个位置办公室里、班级群里就会不约而同地冒出同一个问题“老师这个‘3.1 OJ’到底是什么意思”。有同学以为是某个必须下载的软件有人以为是某道题目的编号还有人直接去搜索引擎里打“OJ学生网址”然后一脸茫然地问我是不是网站打不开了。其实“3.1 OJ”这个说法在不同学校、不同老师那里指的东西不完全一样但它背后的核心几乎都是一件事第三章第一节的作业需要在在线评测系统上完成提交。OJ全称是Online Judge翻译过来就是在线裁判它是一套自动接收你写的程序代码、自动编译运行、自动比对标准答案的系统。与其把它理解成一个“在线考试网站”不如把它理解成一个特别严格、特别较真的改卷老师——它不看过程只看你提交的程序能不能在限定的时间、限定的内存下对一组又一组隐藏数据给出正确输出。这篇内容不是理论科普而是这些年我在带学生刷OJ、自己远程打比赛、以及折腾各种评测平台时攒下来的实操经验。适合刚接触编程、准备在学校课程OJ上交付第一份作业的新手也适合想系统梳理OJ刷题方法和踩坑点的同学。我会从“3.1”这个章节编号讲到平台选择再讲提交代码时最容易忽略的输入输出细节、调试技巧以及C里智能指针这类工具在OJ场景下到底该怎么用。争取让每个人看完都能少走点弯路。1. “3.1 OJ”到底是什么一次说清楚1.1 章节编号背后藏着一整套教学体系先说“3.1”这个数字。绝大多数大学程序设计教材每周课程的推进节奏是固定的比如第三章讲顺序结构、选择结构、循环结构可能第一节是“程序的基本结构”也可能第一节是“if语句与分支”。老师为了把作业和教材进度对齐习惯性地把题单命名为“3.1作业”然后在在线评测系统里建一个叫“3.1 OJ”的题组。所以当你在百度、微信公众号、课程群里看到“3.1 OJ”三个字时不要把它当成一个固化的平台名称。它更像一种记号代表“跟第三章第一节配套的在线评测练习”。有些评测系统会直接把这个编号做成课程模块比如“C语言程序设计-第3章第1节-基础输入输出”学生登录后看到的就是“3.1 OJ”这个分组。这时候最容易发生的误会是什么是学生以为“OJ”是一个需要另外去注册、另外去收费的网站。实际上绝大多数校内OJ都直接用学号和姓名注册有的平台甚至已经对接了教务系统你只要凭学号自动登录就能看到老师提前建好的作业点。关键是搞清楚自己学校推荐的是哪套系统而不是随随便便去网上搜一个听起来很牛的OJ就开刷因为老师建题单的平台和网上开放平台往往不是同一个题号体系也不通用。1.2 从“听懂课”到“程序能跑”的分水岭从我带课的角度看“3.1 OJ”真正想完成的教学目标不是让学生会背语法而是逼迫大家第一次直面程序设计的真实反馈代码不是写完就结束了你还要让机器愿意接受它。很多同学在课堂上听老师讲“printf从右往左算”“scanf要取地址”都听得很顺一到OJ上连最简单的AB都交不上去。原因很简单课堂听讲是线性接收知识OJ是把你推到真实运行环境中去验证知识。你用VC6.0本地写代码时编译器宽容输出乱了一点也无所谓窗口关掉就看不见了但OJ不一样你的程序跑完返回结果会被精确地和标准输出文件做逐字节比对多一个空格、少一个换行、末尾多了个回车全部算错误。这就像你拿到了驾照理论考试的满分但第一次上路却不敢踩油门。“3.1 OJ”就是那条练习场里的车道它不评价你的天赋只看你能不能稳定地完成“读入—计算—输出”这个最基本动作。2. 选OJ平台不是越多越好2.1 那些常被搜索到的OJ平台到底各有什么特点被热搜词反复提到的“江南OJ”“杭电OJ”“湘潭大学OJ”“华为OJ”“东方博宜OJ”其实分属不同的定位。杭电OJ是杭州电子科技大学的老牌在线评测平台题量非常大从入门到竞赛级别都有很多经典题像“1002”“1020”“1096”都是几代学生刷过的习题。它的特点是题目风格老旧但经典适合训练基本功。杭电OJ 1002是“AB Problem II”很多人第一次接触大数加法就是用这道题认识到了int不够用1020和1096也是典型的输入输出套路题专门训练读多组数据、控制空行输出。东方博宜OJ则是中小学信息学竞赛常用的训练平台很多初中生、高中生在上面刷题所以搜“东方博宜OJ答案1065”“东方博宜OJ答案1168”的往往不是大学生而是参加信息学奥赛训练的中学生。这类平台题目偏基础能帮你把循环、数组、递归练扎实。江南OJ、湘潭大学OJ则更多是校内教学使用的在线评测系统界面并不是特别华丽但胜在题单和课程绑定紧密老师布置的作业一般就挂在上面。它们的常见用法比较简单老师提前把题目放进课程题库学生登录后按章节开始作答平台会自动显示通过率。至于华为OJ属于企业招聘场景下的在线编程评测。它的题目不像学校OJ那样把知识点切得那么细更多是考察综合的算法能力和快速编码能力比如字符串处理、链表操作、动态规划这些题目会在机考时给你一个网址要求在限定时间内通过全部测试点。搜索“华为OJ编程”的大概率是应届生在准备招聘机考而不是在刷课程作业。2.2 我建议的选型参考表平台类型代表主要用途适合人群高校教学型江南OJ、湘潭大学OJ、校内平台课程作业、章节练习、考试大一到大二在校生经典题库型杭电OJ编程入门、算法基础、竞赛热身想系统刷题的自学者信息学竞赛型东方博宜OJ中小学信息学训练、CSP/NOIP预备初中生、高中生、竞赛入门者企业招聘型华为OJ、牛客网企业题库笔试、机考模拟应届生、社招跳槽者选平台的建议只有一个先跟课程走再考虑拓展。如果你只是想完成“3.1 OJ”的作业那就老老实实登录学校要求的系统别觉得它界面土、反应慢就自己换个平台刷。因为老师布置的题单、分、截止时间都在那个平台上你在杭电OJ刷一百道校内OJ那道特定题目的提交记录仍是空白期末成绩照样没有分数。3. 从零到提交刷OJ最核心的3个痛点3.1 输入格式看不懂代码写什么都没用我观察过很多新生提交第一份OJ代码最大的问题不是不会写计算逻辑而是根本不知道程序应该怎么读数据。OJ题目描述里经常会有一行“Input”它不是摆设它决定了你的程序从第一行开始要怎么组织。拿最常见的多组输入来举例。题目要求“每组输入两个整数直到EOF结束”你如果只写一次scanf(%d%d,a,b);printf(%d\n,ab);那程序跑完第一组数据就会退出OJ输入文件里剩下的所有数据都不会被处理结果必然是答案错误或者部分正确。正确写法一般是这样#include stdio.h int main() { int a, b; while (scanf(%d%d, a, b) ! EOF) { printf(%d\n, a b); } return 0; }还有一种常见格式是先给一个T表示后面有T组测试数据。比如“第一行输入一个整数T表示测试数据的组数”这时候要用scanf(%d,T); while(T--) { ... }去循环读取。这两种输入模式在3.1阶段的作业里出现频率极高很多经典题就是专门练这个。我教书的时候一再强调拿到一个OJ题先花两分钟看Input、Output两段再想怎么写代码。因为OJ题目的本质就是“给一坨数据让我程序吐出一坨固定格式的结果”你连输入格式都没吃透就算算法再对程序读不到完整数据也白搭。3.2 边界条件和输出格式看起来微不足道扣分却毫不留情搜索热词里出现了“杭电OJ 1002”、“杭电OJ 1020”、“杭电OJ 1096”这三个题实际上都栽过无数人。杭电OJ 1002是AB的大数版本数字长度可能达到非常长明显不能用int、long long硬扛必须用字符串模拟竖式加法或者用高精度数组处理。这就是第一个痛点如果只拿题目当普通AB做你会反复WA想破头也找不到原因。真正的难点是处理进位、处理两个数长度不一致、处理最高位的进位。等到你把这道题调通你对字符串、数组、进位这三个概念的理解会直接上一个台阶。杭电OJ 1096这类题则是输出格式的典型坑。它要求每组输出后都空一行但最后一组后面可能又不需要额外空行或者每一行的行末不能有多余空格。很多同学本地看输出“挺整齐的”一提交就“Presentation Error”原因就是程序多打了一个换行或者少打了一个空行。OJ比对的是字节流不是“人眼看起来差不多就行”。所以说刷“3.1 OJ”这类基础作业本质上是帮你养成分毫不差的习惯。本地测试过了不代表能过你需要反复检查边界数据0有没有考虑负数呢输入最大值有没有超范围多组之间变量有没有清零这些如果在第一次做作业时就养成习惯后面做动态规划、图论算法题会轻松非常多。4. 提交运行后的现场排查实录4.1 常见提交状态速查表第一次用OJ的人最容易被状态栏里的英文搞懵。我把最常见的结果整理成一张表可以帮助你少查阅很多文档状态含义常见原因Accepted通过恭喜这道题过了Wrong Answer答案错误逻辑不正确、边界没考虑、读入不完整Presentation Error输出格式错误多空格、多空行、末尾少回车Time Limit Exceeded超出时间限制循环无限、算法太慢、递归没出口Runtime Error运行时错误数组越界、除零、栈溢出、野指针Compilation Error编译错误语法错误、选错语言、头文件缺失印象最深的是有个学生交的代码一直Runtime Error他本人怎么也想不通明明自己的CodeBlocks里跑得好好的。后来我把代码拿过来一看数组大小是100题目数据范围却是10000越界访问在本地编译器里可能不报错到了OJ的判题环境就直接段错误。这类问题的排查思路很简单把所有数组按题目的最大边界再放大5到10倍把递归深度过大的写法改成循环把指针操作加上越界判断。4.2 在线调试的几个笨办法反而最稳OJ不是本地IDE不能断点调试很多新手一遇到错误就不知道该怎么办了。我的建议是不要硬猜先用最笨的三板斧。第一板斧把题目样例输入手打一遍放到本地运行看输出和样例是否完全一致包括空格和换行。很多时候你以为自己输出对了实际用编辑器显示空白字符才发现行末多了个空格。第二板斧自己构造边界测试数据。比如写了求和的题就试一下输入0写了循环判断的题就试一下循环次数为1和最大值的场景写了字符串处理的题就试一下空字符串和超长字符串。我在指导“3.1 OJ”作业时反复强调要有一点点“杠精精神”专门跟自己的代码过不去。第三板斧加临时printf打印中间结果。测完之后要把调试输出删掉否则这些多余内容会干扰OJ的答案比对导致本来算法正确的结果被判为Wrong Answer。这个小细节很不起眼但每年都有学生死在这里。5. C与OJ智能指针到底该不该用5.1 智能指针在算法题里不是万金油搜索热词里有个关键组合西北农林科技大学C OJ智能指针。这说明在很多学校的数据结构、C程序设计课程里老师会专门布置一道关于智能指针的题目。那智能指针在OJ里到底适合干嘛首先要分场景。如果你是参加ACM、蓝桥杯这类算法竞赛特别是题量很大的情况下我一般不推荐你用unique_ptr、shared_ptr去管理算法中的临时对象。原因是O1处理的核心是数据结构和算法不是你手动释放了多少内存智能指针确实能防内存泄漏但它会引入额外的构建析构开销和代码复杂度在时间限制严格的题目里可能得不偿失。竞赛选手普遍喜欢用裸指针加数组池来模拟链表树结构本质上就是为了极限性能和更简单的内存布局。但如果你是在完成学校OJ里“C面向对象与智能指针”这个章节的作业那情况就完全不同了。题目很可能要求你实现一个链表或者二叉树并且用std::unique_ptr或std::shared_ptr来管理节点资源这时候就非常有必要掌握现代C的内存管理语义。5.2 如何在OJ题目里安全使用智能指针举一个比较常见的学生作业场景题目要求用链表存储一组数据并且要求“按顺序输出所有节点程序结束后不能出现内存泄漏”。如果用裸指针你得一个个delete如果用unique_ptr就可以把节点定义写成这样#include iostream #include memory struct Node { int data; std::unique_ptrNode next; Node(int val) : data(val), next(nullptr) {} }; int main() { auto head std::make_uniqueNode(1); head-next std::make_uniqueNode(2); head-next-next std::make_uniqueNode(3); Node* cur head.get(); while (cur) { std::cout cur-data; if (cur-next) std::cout ; cur cur-next.get(); } return 0; }这里的核心点在于当head析构时unique_ptr会自动递归释放后续节点不需要手写析构逻辑。但有一个坑特别容易踩如果你用Node* cur head.get()去遍历链表每次循环结束应当让cur指向cur-next.get()而不是去移动unique_ptr本身否则可能把所有权意外转移或者中途悬空。至于shared_ptr在OJ题里除非题目明确要求“多个指针共享同一个节点”否则能不用就不用。shared_ptr需要通过引用计数来维护生命周期多一些原子操作开销也会让代码的可读性下降。我自己在带数据结构作业时会跟学生说题目要求智能指针你就老老实实用题目没提你就选最简单的写法。5.3 现代C给OJ带来的便利智能指针只是现代C的一部分。在3.1阶段的作业里我更推荐熟练掌握vector、string、map和sort这类标准库组件。它们相对于手写数组和排序能显著减少错误率。比如读入未知个数的整数并排序用C语言你得自己管理数组大小用C就可以这样写#include bits/stdc.h using namespace std; int main() { vectorint v; int x; while (cin x) { v.push_back(x); } sort(v.begin(), v.end()); for (size_t i 0; i v.size(); i) { if (i) cout ; cout v[i]; } return 0; }很多初学C的同学总觉得用vector是“偷懒”其实这恰恰是正确做法。OJ题目考察的是解决问题能力不是考察你手写动态数组有多溜。能够用合适的高级抽象把问题解决这本就是工程师的基本素养。6. 最后说几句掏心窝的话每年批改“3.1 OJ”的作业我都会看到两类截然不同的学生。一类人把题目发到群里第一句就是“有没有答案”另一类人安静地把题目截图下来自己在草稿纸上画数据流等别人都在聊天的时候他已经提交通过了。相比天赋我更相信刷OJ这件事稳定比聪明更重要。我见过不少过了这道“3.1”作业的人后面依然会在第4章、第5章栽跟头这很正常。因为OJ不是通关游戏它是耐力跑。你今天掌握了一个输入输出的写法明天还要面对更复杂的边界条件你今天避开了大数加法这个坑明天还会遇到更隐蔽的位运算溢出。但只要你把“3.1”这个环节打扎实了后面所有的问题都只是时间和练习量的问题。最后分享一个我自己一直用的笨办法每次做完一道OJ题不管AC还是WA都花30秒在文本文件里记录三件事题目考察的知识点、我犯的错误、正确的解法思路。坚持记录两三个学期之后你会发现自己的错误类型高度集中真正需要警惕的从来不是题目难而是同一个坑反复跳。