跳转至

高级数据格式

高级数据格式用于特殊题目。特殊题目包括使用自定义比较器的题目、交互题、提交答案题、启用子任务的题目。

下发文件

可以在题目文件夹中建立 download 文件夹,并放入任何文件。这些文件不会影响评测,但可从 OJ 下载。

下发文件不包含在题目文件夹中,请阅读 OJ 相关文档以了解更多。

自定义比较器

自定义比较器(SPJ)用于比较每个测试点的答案文件和选手的输出文件,并给出该测试点的状态(Accepted / Wrong Answer)以及得分。

除了内建比较器之外,自定义比较器也可以被使用。OJ 支持使用 testlib 接口的自定义比较器。

为了启用自定义比较器,需要添加兼容 testlib 接口的自定义比较器文件 chk.cpp,然后删除题目配置文件中的 use_builtin_checker 配置项。

在评测时,评测系统会使用 g++ -O2 -std=c++17 -I testlib_dir 命令来编译 chk.cpp,然后将编译结果作为自定义比较器。其中,testlib_dir 是个文件夹,其中只有 testlib.h 一个文件。

使用自定义比较器可以返回 Accepted(满分)、Wrong Answer(零分)、Wrong Answer(部分分)三种状态。具体使用方法如下:

状态 testlib 函数 说明
Accepted quitf(_ok, "消息") 该测试点满分
Wrong Answer(零分) quitf(_wa, "消息") 该测试点零分
Wrong Answer(部分分) quitp(得分比例, "消息") 得分比例([0,1] 之间的实数)= 该测试点得分 / 该测试点总分
无需关心舍入误差问题,OJ 会将得分比例乘以测试点总分后,四舍五入到两位小数

请注意:

  • 当任何 testlib 函数失败时(如 readInt() 遇到文件结尾),testlib 会输出 FAIL,此时 OJ 将返回 Wrong Answer(零分)。
  • 如果你的自定义比较器使用了其他方式返回结果,那么 OJ 对该测试点的评测结果将是一种未定义的行为。(在当前版本中,OJ 可能返回 Wrong Answer(零分))
  • 如果你的自定义比较器运行时发生了错误,那么 OJ 会将该测试点或该提交的评测结果设为 Special Judge Error 或 System Error。

为了方便调试自定义比较器,此处给出 OJ 运行自定义比较器的相关细节:

  • 运行命令行参数:<自定义比较器> <测试点输入文件> <选手输出文件> <测试点答案文件>
  • 运行环境:沙箱,具有与选手程序相似的系统资源限制。
  • 时间和内存限制:由评测系统的具体部署情况决定,通常不低于 5s、512MB。
  • 运行完成后,检查自定义比较器的标准错误输出:若为 "ok " 开头,则判为 Accepted;若为 "points " 开头,则根据后续的实数(范围 [0,1])给出部分分;其他情况判为 Wrong Answer。

本文档不负责 testlib 本身的使用问题,如有困难,请结合 testlib 的官方文档 和上述信息进行本地调试。

如果不想使用 testlib?

自定义比较器可以不包含 testlib 头文件。你可以编写任何自定义比较器,只要它能满足上文中 “命令行参数” 和 “标准错误输出” 的要求,并能在上述运行环境和限制中正常运行。

交互题(联合编译)

交互题(联合编译)提供一个交互库和若干头文件。选手和交互库均可使用这些头文件,且选手源程序和交互库源程序一起编译。

为了启用交互题(联合编译)支持,需要:

  1. 在题目配置文件中添加配置项 with_implementer on
  2. 在题目文件夹中建立 require 文件夹,在该文件夹中保存交互库文件 implementer.cpp
  3. require 文件夹中保存其他可被选手源程序和交互库使用的头文件 *.h

在评测时,首先使用 g++ 编译交互库,编译选项为 -c -O2 -std=c++17,生成对象文件(.o)。然后将该对象文件和选手源程序一起编译,生成选手二进制程序。随后的运行过程同传统题。

在编译交互库和选手程序时,编译选项都会包含 -I require,以便于编译器找到上述头文件。

注意

交互题(联合编译)仅支持 C、C++ 语言。一旦启用交互题(联合编译)支持,除 C、C++ 外的其他语言将无法使用。

交互题(输入输出)

交互题(输入输出)提供一个“交互对端”。选手程序和交互对端同时运行,两者通过标准输入输出进行交互。

为了启用交互题(输入输出)支持,需要:

  1. 在题目配置文件中添加配置项 with_interactor on
  2. 在题目文件夹中建立 require 文件夹,在该文件夹中保存交互对端文件 interactor.cpp
  3. require 文件夹中保存其他可被交互对端使用的头文件 *.h

在评测时,首先使用 g++ 编译交互对端,编译选项为 -O2 -std=c++17 -I require,生成二进制程序,然后编译选手程序。编译完成后,评测系统会同时运行选手程序和交互对端,两者的标准输入输出会被连接,也就是说,选手程序的标准输出会被交互对端的标准输入读取,反之亦然。运行结束后,评测系统采集交互对端的标准错误输出(而不是选手程序的标准输出)用于得分判定。

需要注意的是:

  1. 运行时间限制(CPU 时间):选手程序同题目配置,交互对端为 5 秒,且这两个限制独立。两者的 real time 也具有一定的限制,如果超过限制,评测系统会将其判为超时。
  2. 运行内存限制:选手程序同题目配置,交互对端为 1024 MB,且这两个限制独立。
  3. 输入文件:选手程序无法访问题目配置中的输入文件;交互对端可以通过访问文件系统中当前工作路径下的 input.txt 来读取输入文件。请不要试图访问其他文件,否则可能被判为运行错误(如果是选手程序)或 System Error(如果是交互对端)。
  4. 得分判定:评测系统收集交互对端的标准错误输出,并根据其内容判定得分。具体判定规则由内建比较器或自定义比较器决定。
  5. 评测结果:如果交互对端超时、超内存或运行错误,评测系统会给出 System Error;其他情况按非交互题的方式给出评测结果。
  6. 运行环境:仅支持 Linux,请在 OJ 中为此题目配置适用于 Linux 的编程语言。

另外,交互题(输入输出)和交互题(联合编译)不冲突,可以同时启用。

提交答案题(暂不支持)

注意

评测系统暂不支持提交答案题。如果的确需要提交答案题,请联系相关负责人,申请“一事一议”方案。

选手在提交答案题中不提交源程序,而提交输出文件,评测系统将直接判断输出文件的正确性。

为了启用提交答案题的支持,只需要在题目配置文件里添加配置项:

submit_answer on

请注意,一道题不能同时成为提交答案题和交互题。

注意

启用提交答案题的设置后,请联系 OJ 管理员。

子任务

启用“子任务”特性后,一道题将被划分成若干个子任务,每个子任务包含若干测试点。在评测时,各测试点逐一运行,然后各子任务进行整体评分。评分规则视“子任务类型”(详见下节)决定。

为了启用子任务,需要:

  1. 在题目配置文件中添加配置项 n_subtasks <子任务个数>
  2. 对于每个子任务,在题目配置文件中添加配置项
subtask_score_<子任务编号> <子任务总分>

subtask_end_<子任务编号> <子任务的最后一个测试点编号>

以上子任务编号、测试点编号均从 1 开始。子任务个数是一个不超过 100 的正整数。 子任务总分是一个 [0.01,100] 之间的实数(将被四舍五入至两位小数),且所有子任务的总分之和必须等于 100。

在上述配置中,第 i 个子任务的测试点编号范围是:

[subtask_end_{i-1} + 1, subtask_end_{i}]

以下是子任务配置的一个例子:

n_tests 20
n_subtasks 2
subtask_score_1 30
subtask_end_1 5
subtask_score_2 70
subtask_end_2 20
(省略其他配置项)

该例子描述的子任务配置如下:

  • 共 20 个测试点,编号为 1 ~ 20
  • 共 2 个子任务
  • 第一个子任务包含 5 个测试点,编号为 1 ~ 5,总分 30 分
  • 第二个子任务包含 15 个测试点,编号为 6 ~ 20,总分 70 分

子任务类型

子认为分为“捆绑测试”和“最低得分”两种类型。

  • “捆绑测试”:子任务内的所有测试点必须全部通过(Accepted),该子任务才能通过,否则该子任务记为 0 分。
  • “最低得分”:子任务内的所有测试点按照“满分 100 分”进行测试和评分,然后将上述得分中的“最低分”缩放至该子任务的满分,记为子任务的分数。

所有子任务默认为“捆绑测试”类型。如需使用“最低得分”,请给每个需要的子任务添加如下配置:

subtask_type_<子任务编号> min

子任务依赖关系

OJ 支持“子任务依赖”功能,以避免重复评测多个子任务共用的测试点。该功能可以对每个子任务设置它的“依赖集合”。在测试第 i 个子任务之前,被它依赖的子任务集合将首先被测试,且它们将被加入第 i 个子任务的评测结果中。例如,对于“捆绑测试”,如果被依赖的子任务未通过,则第 i 个子任务也未通过。

子任务依赖的配置项如下:

  1. subtask_dependence_<i> none 子任务 i 的依赖集合为空集(默认);
  2. subtask_dependence_<i> <d> 子任务 i 的依赖集合为 {子任务 <d>}
  3. subtask_dependence_<i> many 子任务 i 的依赖集合大小不小于 1,其具体内容由配置项集合 { subtask_dependence_<i>_<j> <d> } 指定。

以下是子任务依赖配置的一个例子:

n_subtasks 4
subtask_dependence_2 1
subtask_dependence_4 many
subtask_dependence_4_1 2
subtask_dependence_4_2 3
(省略其他配置项)

该例子描述的子任务依赖关系如下:

  • 共 4 个子任务,编号为 1, 2, 3, 4
  • 子任务 1 依赖:空集
  • 子任务 2 依赖:{子任务 1}
  • 子任务 3 依赖:空集
  • 子任务 4 依赖:{子任务 2, 子任务 3}

测试点独立分值

未开启子任务功能时,可以给每个测试点设置不同的得分:

  • test_score_<i> 表示第 i 个测试点的分数([0.01,100] 范围内的实数,将被四舍五入至两位小数)。
  • 总分为 100 分,未指定分数的测试点均分剩下的分数(以 0.01 分为粒度进行分配,若不能均分,则不同测试点分值升序排列,且极差为 0.01)。

测试点/子任务独立时间内存限制

每个测试点、每个子任务可以设置不同时间限制、内存限制:

  • subtask_time_limit_<i> 表示第 i 个子任务的时间限制。
  • subtask_memory_limit_<i> 表示第 i 个子任务的内存限制。
  • test_time_limit_<j> 表示第 j 个测试点的时间限制。
  • test_memory_limit_<j> 表示第 j 个测试点的内存限制。

其中,i 为子任务编号(范围 [1, n_subtasks]),j 为测试点编号(范围 [1, n_tests])。

考虑到一个测试点同时受到“测试点限制”、“子任务限制”、“题目限制”,会产生冲突,OJ 定义第 j 个测试点的时间限制(内存限制)如下:

  • test_time_limit_<j>test_memory_limit_<j>),如果存在。
  • 否则,subtask_time_limit_<i>subtask_memory_limit_<i>),如果开启子任务功能,且第 j 个测试点属于第 i 个子任务,且这一项存在。
  • 否则,time_limitmemory_limit),即题目的时间限制(内存限制)。

特别地,如果每个测试点都可以由 test_*_limitsubtask_*_limit 获得其时间限制(内存限制),则题目的时间限制(内存限制)可以不存在。

提交答案题无需设置任何测试点的时间限制或内存限制。


题目数据格式复杂,建议在上传数据前,先使用“题目配置文件检查器”进行检查。