NCST Online Judge使用教程

NCST Online Judge 是一个供编程爱好者学习、交流和提高的平台

日常训练

查看题库中的题目

点击顶部导航栏中的题库即可查看所有题目。

image

随后网页跳转至题库页,其中显示了题目的通过信息状态(具体状态信息参见下文的评测状态)

image

我们随便打开一道题,可以获得一下基本信息

image

评测状态

  • 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

使用在线代码编辑器

如图,介绍了在线编辑器的基本功能

image

image

使用本地代码编辑器

image

点击题目页的递交,打开递交页。

image

评测后即可返回评测状态

image

注意事项

  • 评测机使用进程的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之类的,以此类推)

image

若采用其他格式可能导致以下渲染结果(不美观,不规范)。为了选手读题时的方便,还望每位出题人记住这一点

image

以下引用于文章如何正确地在 HydroOJ 出题(非官方文档,超级详细)

创建题目

首先进入一个自己有创建题目权限的域(如果没有就创建一个),然后进入题库点击“创建题目”,就进到了编辑界面啦!这个界面相信大家都能看懂,就不多加说明了,写好题面点创建即可。特别地,难度缺省则为自动计算。

接下来会跳到“文件”界面,要求你上传测试点。你可以不立即上传测试点,当然这一步总是绕不过的。下面接着讲测试点的配置,分别举了各种题型当例子,希望可以讲明白 qwq

传统题

我们以 A+B Problem 为例,讲解传统题测试点的配置。

首先你需要写一个数据生成器,并在本地生成出所有测试点,当然 A+B 这种萌萌题直接手打也行。特别提醒,如果用 time(0) 之类的作为随机数种子,为了保证数据强度,请确保没有两组数据在同一秒生成。(好像跑题了

把测试点保存为 plus1.inplus1.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.inxxx1.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 练习准备我们还是配置一下。我才不会说是我造完这个才发现有过了。

A+B Problem

这个的实现不难想,拿 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)

不知道为啥它挂了。

特殊题目

Quine

经典的非传统题了,写一个程序输出自己源代码,包含至少 10 个非空格的可见字符。

没找到现成的题,自己造的。

小知识:HydroOJ 供 SPJ 获取的存放源代码的文件叫 user_code

准备一组空的 1.in1.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 试试吧!

参加比赛

点击顶部导航栏的比赛即可进入比赛页面。

image

选择进入一个比赛,得到以下页面。点击顶部导航栏的题目列表即可查看比赛题目

image

查看比赛题目,随后使用方法和日常训练同理

image

讨论和分享

参考洛谷讨论区用法,不做详细解释,非核心功能。

0 条评论

目前还没有评论...