- Hydro
NCST Online Judge使用教程
- 2024-6-29 3:07:43 @
NCST Online Judge使用教程
NCST Online Judge 是一个供编程爱好者学习、交流和提高的平台
日常训练
查看题库中的题目
点击顶部导航栏中的题库
即可查看所有题目。
随后网页跳转至题库页,其中显示了题目的通过信息状态(具体状态信息参见下文的评测状态)
我们随便打开一道题,可以获得一下基本信息
评测状态
- Waiting 评测:评测请求正在等待被评测机抓取
- Fetched 评测:评测请求已被评测机抓取,正在准备开始评测
- Compiling 评测:正在编译中
- Judging 评测:编译成功,正在评测中
- Accepted 通过:程序输出完全正确
- Wrong Answer 不通过:程序输出与标准答案不一致(不包括行末空格以及文件末空行)
- Time Limit Exceeded 不通过:程序运行时间超过了题目限制
- Memory Limit Exceeded 不通过:程序运行内存空间超过了题目限制
- Runtime Error 不通过:程序运行时错误(如数组越界、被零除、运算溢出、栈溢出、无效指针等)
- Compile Error 不通过:编译失败
- System Error 错误:系统错误(如果您遇到此问题,请及时在讨论区进行反馈)
- Canceled 其他:评测被取消
- Unknown Error 其他:未知错误
- Ignored 其他:被忽略
有“成绩取消”字样则说明管理员手动标记此记录为取消,可能违反了服务条款,比如代码被发现与其他用户的代码十分相似。
常见缩写如下
原文 | 缩写 |
---|---|
Accepted | AC |
Wrong Answer | WA |
Time Limit Exceeded | TLE |
Memory Limit Exceeded | MLE |
Runtime Error | RE |
Compile Error | CE |
使用在线代码编辑器
如图,介绍了在线编辑器的基本功能
使用本地代码编辑器
点击题目页的递交,打开递交页。
评测后即可返回评测状态
注意事项
-
评测机使用进程的CPU时间计算时间消耗。也就是说,异步多线程算法的计算时间会包括子线程的运行时间。
-
若无特殊说明,Online Judge 上均采用标准输入输出(控制台输入输出,屏幕输入输出,STD I/O)
-
在 Online Judge 上做题的时候,不要画蛇添足,例如打印多余信息。如以下代码(其他语言同理)
# Python name = input("请输入你的名字") # 错误,输出了冗余信息“请输入你的名字”。 print("hello " + name);
// C/C++ #include <stdio.h> int main() { char name[20]; printf("请输入你的名字\n"); // 错误,还是输出了冗余的信息,应该删掉这行代码 scanf("%s", name); printf("hello %s", name); return 0; }
出题指导
出题格式注意事项
在编辑样例输入时,采用以下格式
## **样例输入输出**
```input1
3 3
0 3
1 2
0 2
```
```output1
3 1 2
```
最终渲染的效果如下(如果有多种样例就写input2
, input3
之类的,以此类推)
若采用其他格式可能导致以下渲染结果(不美观,不规范)。为了选手读题时的方便,还望每位出题人记住这一点
创建题目
首先进入一个自己有创建题目权限的域(如果没有就创建一个),然后进入题库点击“创建题目”,就进到了编辑界面啦!这个界面相信大家都能看懂,就不多加说明了,写好题面点创建即可。特别地,难度缺省则为自动计算。
接下来会跳到“文件”界面,要求你上传测试点。你可以不立即上传测试点,当然这一步总是绕不过的。下面接着讲测试点的配置,分别举了各种题型当例子,希望可以讲明白 qwq
传统题
我们以 A+B Problem 为例,讲解传统题测试点的配置。
首先你需要写一个数据生成器,并在本地生成出所有测试点,当然 A+B 这种萌萌题直接手打也行。特别提醒,如果用 time(0)
之类的作为随机数种子,为了保证数据强度,请确保没有两组数据在同一秒生成。(好像跑题了
把测试点保存为 plus1.in
、plus1.out
等的形式,注意文件名中必须带有数字,否则可能无法正确识别。
然后把这些测试点文件拖到题目的“文件”界面里的“测试数据”,就上传上去了,当然强大的 HydroOJ(不是打广告)还支持在线编辑文件。
然后呢?然后就没了,快点击“递交”测一测你的 A+B 吧~
如果需要自定义单个测试点分数,或者时空限制,你需要创建一个 config.yaml
,包含如下内容:
score: 20 # 单个测试点分数
time: 1s # 时间限制
memory: 256m # 内存限制
客观题
我们以 1+1 Problem 为例,讲解客观题测试点的配置。
HydroOJ 支持单选题和填空题,大概是出初赛题用的吧。
在编辑题目界面,在题面里写如下内容:
- desc: 请从下面所给的 A、B、C 三个选项中选择最佳选项。
choices:
- A. 1 + 1 = 1
- B. 1 + 1 = 2
- C. 1 + 1 = 3
- desc: 请完成填空:1 + 1 = ?
就准备好了一半。这部分大家对照着我的题面看看就能知道是什么意思了。
由于这题的测试点配置比较独特,我们不再需要 xxx1.in
、xxx1.out
这种东西,只需要一个测试点配置文件 config.yaml
。
对于客观题来讲,文件配置大致如下:
type: objective # 告诉评测机这题是一道客观题
outputs: # 答案列表,格式是 [答案, 分值]
- [B. 1 + 1 = 2, 50] # 选 B. 1 + 1 = 2,得 50 分
- ['2', 50] # 填 2,得 50 分
然后题面中的 desc
之类的奇怪东西就被替换成我们想要的单选框个填空了。
文件读写
如果题目用在模拟赛里的话,可能希望模拟真实比赛环境,加上文件读写,这个 HydroOJ 也是支持的。
我们依然举 A+B Problem 为例子,这时候我们希望选手们从 plus.in
而不是标准输入读入数据,并将答案写到 plus.out
而不是标准输出。
类似于上面“传统题”部分讲的,先把测试点上传上去,然后由于特殊需求,我们也需要写一个 config.yaml
。
这个文件里面只需要写明希望操作的文件名就好了,其他缺省会默认成传统题的一般配置:
filename: plus
子任务和子任务依赖
我们依然以 A+B Problem 为例(谁叫这个最简单呢
害怕脚造数据,或者有时候遇到这种困难,就是不同的乱搞的最差情况不同,卡了一个就放了另一个?没关系,我们有子任务!
一个乱搞过了最大的部分分,却在较强的小数据挂掉了?HydroOJ 还支持子任务依赖,就是只有通过了某些前置子任务,这个子任务才会计分,否则计 0 分。
在 config.yaml
里面如下配置:
subtasks: # 表示本题采用子任务
- score: 20 # 这个子任务分值
id: 0 # 子任务编号
# type: min # min/max/sum,表示子任务得分怎么由所包含测试点计算得到,缺省默认 min
# time: 1s # 可以给每个子任务设置不同的时空限制
# memory: 256m
cases: # 子任务包含的测试点列表
- input: plus1.in
output: plus1.out
- score: 40
id: 1
cases:
- input: plus2.in
output: plus2.out
- input: plus3.in
output: plus3.out
- score: 40
id: 2
if: [0, 1] # 子任务依赖,这个子任务得分需要 id 为 0、1 的两个子任务都对
cases:
- input: plus4.in
output: plus4.out
- input: plus5.in
output: plus5.out
小技巧:如果把测试数据命名为 xxx1-1.in xxx1-2.in xxx2-1.in xxx2-2.in 这种格式,就会自动归类 subtask
自定义校验器(Special Judge)
依然是 A+B Problem,这题没有 SPJ 的必要,只是作为示例解释如何使用。
首先你需要写一个 checker.cc
(名字可以随便起,注意不是 .cpp
),例如:
#include "testlib.h"
int main(int argc, char* argv[]) {
setName("compares two signed integers");
registerTestlibCmd(argc, argv);
int ja = ans.readInt();
int pa = ouf.readInt();
if (ja != pa)
quitf(_wa, "expected %d, found %d", ja, pa);
quitf(_ok, "answer is %d", ja);
}
然后在 config.yaml
里面注明使用 SPJ 评测:
checker_type: testlib # 根据官方文档,支持 default(忽略行末空格和文末回车), ccr, cena, hustoj, lemon, qduoj, syzoj, testlib,可以选用自己熟悉的,但我只用过 testlib
checker: checker.cc
PDF 题面
如果题目用在模拟赛的话,可能也希望使用 PDF 题面,这也是支持的 Link。
首先要在我的文件(Link)上传 PDF 文件(其他格式也成),注意是我的文件而不是题目文件。
然后题面这么写就行:
@[doc](https://hydro.ac/d/rui_er/file/44/statement-a-plus-b.pdf)
记得把 url 改成自己上传的文件的。
如果有需要展示 PPT 的话,把上面那行的 doc
改成 slide
就行。
ACM 赛制
这里说的不是比赛的赛制,而是题目的赛制。
HydroOJ 的比赛选 ACM 赛制好像一切问题都解决了,不过为了 ACM 练习准备我们还是配置一下。我才不会说是我造完这个才发现有过了。
这个的实现不难想,拿 config.yaml
把所有测试点塞到一个子任务里,这个子任务记 1 分即可。
subtasks:
- score: 1
id: 0
cases:
- input: plus1.in
output: plus1.out
- input: plus2.in
output: plus2.out
- input: plus3.in
output: plus3.out
- input: plus4.in
output: plus4.out
- input: plus5.in
output: plus5.out
理论上如果是省选以下模拟赛出题人之类的,看到这里就够了,下面是一些特殊题目的配置方法。
提交答案题
单文件提答
A+B Problem,这次我把输入都给你了,求出来输出之后告诉我。我不要程序,只要输出。
由于是单文件提答,我们要求你只提交一个文件,在每一行给出每个问题的答案。
只造一组数据(可以考虑多测来放多组),然后显然需要配置一下 config.yaml
:
type: submit_answer # 告诉评测机这是个提答题
这就完了?确实。
提交方法比较不友善,点进递交发现还是要选代码语言,咋办?交输出还是交代码?
让你交输出就交输出啊,随便选个你觉得可爱的语言直接交就行,就这样:
2919
3
18
12958
19992
多文件提答
A+B Problem,上传数据的时候格式不太一样,由于是提答题评测机不想要你的输入文件,因此输入文件内容改成希望从压缩包中读取的文件名称如 plus1.out
,输出文件不变。
至于 config.yaml
,你还需要告诉评测机是多文件提答,如下:
type: submit_answer
subType: multi
交互题
Grader 交互(函数式交互)
这里吐槽一句:测试题库里面那个函数式交互根本不是比赛中的函数式交互好吗。。
于是自己造轮子,搞一个真正的 Grader 交互的 A+B Problem。
我们先准备好 plus.h
:
//By: Luogu@rui_er(122461)
int inc(int);
int dec(int);
int myPlus(int, int);
然后是我们的 Grader,这里叫 plus.cc
:
//By: Luogu@rui_er(122461)
#include "plus.h"
#include <bits/stdc++.h>
using namespace std;
int inc(int x) {return x + 1;}
int dec(int x) {return x - 1;}
int main() {
int x, y;
assert(scanf("%d%d", &x, &y) == 2);
printf("%d\n", myPlus(x, y));
return 0;
}
考虑一下这种交互怎么实现,选手提交的代码是一些函数,主函数和判题的一些操作在 Grader 里面,那自然就要把这两个文件编译到一起(多文件编译)。
于是就需要知道交上去的文件被存成了啥名字,我在讨论:(已解决)【提问】HydroOJ 是否支持传统 Grader 交互题中提问了,得到的回答是,C 语言在 foo.c
,C++ 语言在 foo.cc
。
HydroOJ 还支持自定义编译方法:写一个 compile.sh
。
于是就可以实现这一功能了。
最终运行时运行的是 ./foo
,所以多文件编译出来的名字要是这个。
compile.sh
:
g++ foo.cc plus.cc -o foo -O2
config.yaml
:
type: default # 传统题!不是 interactive 交互题!
user_extra_files: # 被放到工作目录下的文件
- compile.sh # 用来编译的
- plus.h # 头文件
- plus.cc # 交互库
这是答案示例:
//By: Luogu@rui_er(122461)
#include "plus.h"
#include <bits/stdc++.h>
int myPlus(int a, int b) {
return inc(a) + dec(b); // 直接 a + b 也行,这只是展示一下可以调用我们给的函数
}
I/O 交互
大概是 CF 等在线网站比较常用的交互方式。
A+B Problem,这时我们需要写一个交互库了。
交互库是干啥的?I/O 交互中是用来处理询问和发送数据的,交互库的标准输入是提交的代码的标准输出,交互库的标准输出是提交的代码的标准输入。
本题的交互库就是这样:
//By: Luogu@rui_er(122461)
#include "testlib.h"
#include <bits/stdc++.h>
#include <random>
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
#define debug printf("Running %s on line %d...\n",__FUNCTION__,__LINE__)
#define fileIO(s) do{freopen(s".in","r",stdin);freopen(s".out","w",stdout);}while(false)
using namespace std;
typedef long long ll;
template<typename T> void chkmin(T& x, T y) {if(x > y) x = y;}
template<typename T> void chkmax(T& x, T y) {if(x < y) x = y;}
int main(int argc, char* argv[]) {
setName("Interactor A+B");
registerInteraction(argc, argv);
rnd.setSeed(time(0)+clock()); // 测试数据不可避免地可能会在同一秒生成,于是下面几行乱搞一下尽量生成得不同,亲测有效
mt19937 myRnd(time(0)+clock()*20);
uniform_int_distribution<int> dist;
rnd.setSeed(time(0)+clock()+rnd.next(0, 10000)+dist(myRnd)+dist(myRnd));
int a = rnd.next(0, 10000); // 生成数据
int b = rnd.next(0, 10000);
printf("%d %d\n", a, b); // 发送给提交的程序
fflush(stdout); // 记得刷新缓冲区!记得刷新缓冲区!!记得刷新缓冲区!!!
int c;
scanf("%d", &c); // 读进来提交的程序给出的答案
if(a + b == c) quitf(_ok, "Accepted! (%d + %d = %d)", a, b, c); // 并判断
else quitf(_wa, "Wrong answer. (%d + %d = %d, but %d found)", a, b, a+b, c);
return 0;
}
显然也需要一个 config.yaml
,如下:
type: interactive # 交互题
interactor: interactor.cc # 我们的交互库
cases:
- input: /dev/null # 没有输入和答案,数据是交互库动态生成的,所以留空
output: /dev/null
- input: /dev/null
output: /dev/null
- input: /dev/null
output: /dev/null
- input: /dev/null
output: /dev/null
- input: /dev/null
output: /dev/null
- input: /dev/null
output: /dev/null
- input: /dev/null
output: /dev/null
- input: /dev/null
output: /dev/null
- input: /dev/null
output: /dev/null
- input: /dev/null
output: /dev/null
通信题
这个我还没搞好,搞好之后再补,可以先参考 @ HHHE 的博客 Link。
远端评测题(Remote Judge)
不知道为啥它挂了。
特殊题目
经典的非传统题了,写一个程序输出自己源代码,包含至少 10 个非空格的可见字符。
没找到现成的题,自己造的。
小知识:HydroOJ 供 SPJ 获取的存放源代码的文件叫 user_code
。
准备一组空的 1.in
、1.out
,只是占位用,显然这题评测不需要测试点。
类似上面说的 SPJ,我们先配置 config.yaml
:
checker_type: testlib
checker: checker.cc
然后考虑 SPJ 咋写。
我们已经知道咋获取源代码了,就好办多了,直接读文件比较即可,注意去掉行末空格、文末回车。
给个我的实现:
//By: Luogu@rui_er(122461)
#include "testlib.h"
#include <bits/stdc++.h>
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
#define debug printf("Running %s on line %d...\n",__FUNCTION__,__LINE__)
#define fileIO(s) do{freopen(s".in","r",stdin);freopen(s".out","w",stdout);}while(false)
using namespace std;
typedef long long ll;
template<typename T> void chkmin(T& x, T y) {if(x > y) x = y;}
template<typename T> void chkmax(T& x, T y) {if(x < y) x = y;}
int main(int argc, char* argv[]) {
setName("quine checker");
registerTestlibCmd(argc, argv);
string pans = "", jans = "";
ifstream cod("user_code"); // code stream
int cnt = 0, lines = 0;
while(!ouf.eof()) {
++lines;
pans = ouf.readLine();
getline(cod, jans);
int n = jans.length();
for(;jans[n-1]==' '||jans[n-1]=='\n'||jans[n-1]=='\r';--n);
jans = jans.substr(0, n);
if(pans != jans) quitf(_wa, "Wrong answer on line %d. (Expected '%s', but '%s' found)", lines, jans.c_str(), pans.c_str());
for(auto i : pans) if(i >= 33 && i <= 126) ++cnt;
}
if(cnt < 10) quitf(_wa, "Code is too short.");
quitf(_ok, "Accepted! (%d characters)", cnt);
return 0;
}
其他特殊题目
那就要看你具体想干啥了,仿照 Quine 自己写一个 SPJ 试试吧!
参加比赛
点击顶部导航栏的比赛
即可进入比赛页面。
选择进入一个比赛,得到以下页面。点击顶部导航栏的题目列表
即可查看比赛题目
查看比赛题目,随后使用方法和日常训练同理
讨论和分享
参考洛谷讨论区用法,不做详细解释,非核心功能。