高级数据格式
高级数据格式用于特殊题目。特殊题目包括使用自定义比较器的题目、交互题、提交答案题、启用子任务的题目。
下发文件
可以在题目文件夹中建立 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 头文件。你可以编写任何自定义比较器,只要它能满足上文中 “命令行参数” 和 “标准错误输出” 的要求,并能在上述运行环境和限制中正常运行。
交互题(联合编译)
交互题(联合编译)提供一个交互库和若干头文件。选手和交互库均可使用这些头文件,且选手源程序和交互库源程序一起编译。
为了启用交互题(联合编译)支持,需要:
- 在题目配置文件中添加配置项
with_implementer on
; - 在题目文件夹中建立
require
文件夹,在该文件夹中保存交互库文件implementer.cpp
; - 在
require
文件夹中保存其他可被选手源程序和交互库使用的头文件*.h
。
在评测时,首先使用 g++
编译交互库,编译选项为 -c -O2 -std=c++17
,生成对象文件(.o
)。然后将该对象文件和选手源程序一起编译,生成选手二进制程序。随后的运行过程同传统题。
在编译交互库和选手程序时,编译选项都会包含 -I require
,以便于编译器找到上述头文件。
注意
交互题(联合编译)仅支持 C、C++ 语言。一旦启用交互题(联合编译)支持,除 C、C++ 外的其他语言将无法使用。
交互题(输入输出)
交互题(输入输出)提供一个“交互对端”。选手程序和交互对端同时运行,两者通过标准输入输出进行交互。
为了启用交互题(输入输出)支持,需要:
- 在题目配置文件中添加配置项
with_interactor on
; - 在题目文件夹中建立
require
文件夹,在该文件夹中保存交互对端文件interactor.cpp
。 - 在
require
文件夹中保存其他可被交互对端使用的头文件*.h
。
在评测时,首先使用 g++
编译交互对端,编译选项为 -O2 -std=c++17 -I require
,生成二进制程序,然后编译选手程序。编译完成后,评测系统会同时运行选手程序和交互对端,两者的标准输入输出会被连接,也就是说,选手程序的标准输出会被交互对端的标准输入读取,反之亦然。运行结束后,评测系统采集交互对端的标准错误输出(而不是选手程序的标准输出)用于得分判定。
需要注意的是:
- 运行时间限制(CPU 时间):选手程序同题目配置,交互对端为 5 秒,且这两个限制独立。两者的
real time
也具有一定的限制,如果超过限制,评测系统会将其判为超时。 - 运行内存限制:选手程序同题目配置,交互对端为 1024 MB,且这两个限制独立。
- 输入文件:选手程序无法访问题目配置中的输入文件;交互对端可以通过访问文件系统中当前工作路径下的
input.txt
来读取输入文件。请不要试图访问其他文件,否则可能被判为运行错误(如果是选手程序)或 System Error(如果是交互对端)。 - 得分判定:评测系统收集交互对端的标准错误输出,并根据其内容判定得分。具体判定规则由内建比较器或自定义比较器决定。
- 评测结果:如果交互对端超时、超内存或运行错误,评测系统会给出 System Error;其他情况按非交互题的方式给出评测结果。
- 运行环境:仅支持 Linux,请在 OJ 中为此题目配置适用于 Linux 的编程语言。
另外,交互题(输入输出)和交互题(联合编译)不冲突,可以同时启用。
提交答案题(暂不支持)
注意
评测系统暂不支持提交答案题。如果的确需要提交答案题,请联系相关负责人,申请“一事一议”方案。
选手在提交答案题中不提交源程序,而提交输出文件,评测系统将直接判断输出文件的正确性。
为了启用提交答案题的支持,只需要在题目配置文件里添加配置项:
submit_answer on
请注意,一道题不能同时成为提交答案题和交互题。
注意
启用提交答案题的设置后,请联系 OJ 管理员。
子任务
启用“子任务”特性后,一道题将被划分成若干个子任务,每个子任务包含若干测试点。在评测时,各测试点逐一运行,然后各子任务进行整体评分。评分规则视“子任务类型”(详见下节)决定。
为了启用子任务,需要:
- 在题目配置文件中添加配置项
n_subtasks <子任务个数>
。 - 对于每个子任务,在题目配置文件中添加配置项
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
个子任务也未通过。
子任务依赖的配置项如下:
subtask_dependence_<i> none
子任务i
的依赖集合为空集(默认);subtask_dependence_<i> <d>
子任务i
的依赖集合为{子任务 <d>}
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_limit
(memory_limit
),即题目的时间限制(内存限制)。
特别地,如果每个测试点都可以由 test_*_limit
或 subtask_*_limit
获得其时间限制(内存限制),则题目的时间限制(内存限制)可以不存在。
提交答案题无需设置任何测试点的时间限制或内存限制。
题目数据格式复杂,建议在上传数据前,先使用“题目配置文件检查器”进行检查。