参考
本篇文章参考官方文档,想看原版和更详细的内容,请点击官方文档。
B站上也有视频:https://www.bilibili.com/video/BV13A411P7qz
造题流程
首先我们要注册一个账号,登录后点击New Problem创建一个题目,名称无所谓,只能用小写字母、数字和横杠。
创建好之后,点击start开始造题。
General info
这里是进入的第一个页面,用来调整一些基本参数。
题目描述——Statement
使用latex书写并进行排版,我们需要将这个文件替换到statements.ftl中,以便支持中文pdf。
另外对于复杂的latex用法(例如较长的数学公式)最终显示到codeforces上时会渲染不出来,但是在导出的pdf和html文件上都没问题。
当然,如果不在codeforces上举办,我们也可以自己排版然后编译latex。
题面latex模板:https://dluacm.cn/?page_id=411
文件头 —— #include “testlib.h”
这个文件头是在Polygon上造题所必要的文件头(除题解外)。它包含了造题所需要的东西,可以在polygon上下载。
点击上面的Files,然后下载到需要的目录即可。
在Domjudge中放入的文件头和平时造题所用的不是一个,是另外一个testlib-for-domjudge-master的版本。
Generator —— 生成器
我们点击Files,然后上传数据生成器。
#include "testlib.h"//必要头文件
#include <iostream>
using namespace std;
int main(int argc, char* argv[])//必要格式,复制粘贴即可。
{
registerGen(argc, argv, 1);//注册生成器,必要格式。
cout << rnd.next(1, 10) << endl;
//随机生成[1,10]中的一个(闭区间)。
//rnd.next为数据随机函数,随机范围和数据类型和你传的函数有关。
//例如:rnd.next(1ll,10ll)就会变成生成一个long long型的数。
cout << rnd.next("[a-zA-Z0-9]{1,1000}") << endl;
//随机生成字符集为a-z,A-Z,0-9的长度为[1,1000]的字符串。
int length = rnd.next(1, 1000);
cout << rnd.next("[a-zA-Z0-9]{1,%d}", length) << endl;
//范围可以自己定义。
cout<<rnd.next("[0000000001]{100}")<<endl;
//随机字符为0和1,长度为100的字符串其中生成'1'的概率为10%,生成'0'的概率为90%。
return 0;
}
Validator —— 校验器
之后我们需要上传数据校验器,并且在validator页面中启用。
需要注意的是,所有手工上传的数据都会被自动去掉行末空格,添加行末换行(如果没有)。
我们可以在Validator页面下面添加一些人工数据,然后Run Test来检验我们写的数据校验器是否正确(这里的不会被自动修正)。
#include "testlib.h"
int main(int argc, char* argv[])//必要格式。
{
registerValidation(argc, argv);
//注册校验器,必要格式。
inf.readInt(1, 100, "n");
//读取一个[1,10]范围且数据类型为int型的数,如果出错,会提示数据n出错。
//有返回值,返回这个数。
inf.readDouble(1.0,10.0,"n");
//读取一个[1.0,10.0]范围且数据类型为double型的数,如果出错,会提示数据n出错。
//有返回值,返回这个数。
for (int i = 0; i < n; i++)
{
inf.readLong(-1LL, 1000LL, format("a[%d]", i + 1));
//读取一个[-1,1000]范围且数据类型为long long型的数,如果出错,会提示数据a[i]出错。有返回值,返回这个数。
inf.readSpace();
}
inf.readToken("[a-z]{1,100}", "s");//读一个字符串,到空格结束
inf.readString("[a-z]{1,100}", "s");//读一行
//读取一个字符集为a-z,长度为[1,100]的字符串,如果出错,会提示数据s出错。有返回值,返回这个字符串。
ensure(bool x)函数
//如果x为真,正常运行;如果x为假,触发报错。
inf.readSpace();
//读取一个空格,如果没有读取到会提示没有读取到空格。
inf.readEoln();
//读取一个换行,如果没有读取到会提示没有读取到换行。
inf.readEof();//每个文件必定有文件尾,这个是必要的。
//读取一个文件尾,如果没有读取到会提示没有读取到文件尾。
return 0;
}
Checker —— 检验器/特判
上传后在checker页面启用,当然有不少内置比较器可以使用。
篇幅有限,看更多详细的codefoces检验器请点击codeforces检验器
#include "testlib.h"
int main(int argc, char * argv[])//必要格式。
{
registerTestlibCmd(argc, argv);//注册检验器。
int ja = ans.readInt();
//读取标准输出,就是标准答案文件中的数据。
int pa = ouf.readInt();
//读取选手输出,就是要判的代码的输出。
ans.readEof();
ouf.readEof();
//读到标准答案/选手答案的文件尾。
ans.readSpace();
ouf.readSpace();
//读取标准答案/选手答案的空格。
ans.readEoln();
ouf.readEoln();
//读取标准答案/选手答案的换行。
ans.seekEof();
inf.seekEof();
ouf.seekEof();
//判断文件尾
if (ja != pa) quitf(_wa, "expected %d, found %d", ja, pa);
//如果不相等,返回WA,并离开,显示错误原因expected ja, found pa。
quitf(_ok, "answer is %d", ja);
如果相等,返回ok,并离开,显示answer is ja。
string ja="???";
if (ja != "YES" && ja != "NO")//拿YES和NO举例
quitf(_fail, "%s or %s expected in answer, but found %s", "YES".c_str(), "NO".c_str(), ja.c_str());
//表示你的标准答案出错,提示YES or NO expected in answer,but found ja。
string pa="???";
if (pa != "YES" && pa != "NO")
quitf(_pe, "%s or %s expected, but found %s","YES".c_str(), "NO".c_str(), pa.c_str());
//说明选手的答案不符合规格,提示YES or NO expected in answer,but found pa。
return 0;
}
Interactor
因为不常用,鸽了。
Tests
里面可以上传手工数据(样例需要点击后面的Example并且设置为yes),并且可以写数据生成脚本。
脚本格式:【生成器文件名】 【若干个参数】 > $
例如:gen 1000 100000 1415 > $
后面的参数在生成器中可以用argv[]去读取(可以搜索C语言的主函数int main(int argc, char * argv[]) argv的含义),然后会把所有参数作为随机种子,所以最后一定要加一个随机数。
stresses
对拍器,输入数据生成脚本和对拍文件、设置时间之后就可以对拍。
solution files
这里上传标程和其他正确或错误解法。
Invocations
点击上面的what to run solutions去运行生成器。
Packages
生成器运行成功之后,可以在这里打包,打包之后题目就造好了。
Manage access
这里可以给其他用户权限。如果需要在codeforces上做题,需要给codeforces用户权限。
杂项
有可能你会感觉到上面函数有些死板,其实并没有那么死板,死板的只是格式。但是你人是活的,可以通过各种各样的方法使你的代码“变活”。
[a-z],[aaaaaaabssfa]这些是互通的,相同的字符多了会增加生成这个字符的概率。
{1,100},{100}里面不一定需要放两个数,也可以放一个数,让它变成一个固定的值,也可以通过%d等等方法来定义。
还有format(“a[%d]”, i + 1),这只是其中一种用法,你会发现,所有带双引号的内容都可以用format代替,再追溯下去,会发现都可以用String来代替。
argv数组是生成器的脚本代码。
例如:generator 5 10 4276 11035 > 9
argv数组是可以读取的,读出来是一个字符串,可以通过atoi(argv[1])来读取成int型,也可以通过atoll来读取成long long型。以此类推还有argv[2],argv[3]。(argv[0]是”generator”,具体自行搜索c语言argc和argv的作用)
string s;
cin>>s;//s输入[a-zA-Z0-9]{1,1000}。
cout << rnd.next(s) << endl;
感想
代码是死的,人是活的。上面给出的只是造题最常用的,如何造题还要看我们自己去写出自己的特色。
命题一般规范
通用
时间限制和空间限制
1.【强制】不卡未关闭同步流的cin和cout。【建议】不卡java和python。
补充:特殊情况除外,例如大量浮点数读入等等导致二者所差时间极大(极大:为了照顾慢io,使得快速io可以让不符合出题人预期的时间复杂度通过)。
2.【建议】无特殊要求一般空间统一给256或512M,可以从空间限制入手,卡掉某些做法。
3.【建议】由于现代计算机运行速度远大于每秒1e8次,所以设计数据范围应该按照每秒1e10次去设计。例如$O(nlog^2n)$或O(n)算法我们数据范围可以设计为5e5。O(n^2)设计为5000,O(n^3)设计为500,这样无论在快慢机器上都会得出正确的做法。
用语
1.【强制】总体保证描述准确清晰,词汇风格统一(例如不能前面说“数组”后面说“序列”,但对于子序列等问题可以适当处理)。
- 如果做不到建议学习《应用文写作》和《大学语文》
例如以下两个描述:
- 给出一个序列
-
给出一个n个数的数组a
明显第二个描述用语比第一个准确。
2.【强制】大部分情况下应该使用书面语,尽量不要出现口语化的句子。
3.【强制】题目描述语句通顺、表意清晰,不能出现病句或阅读不通顺的句子,保证不会前后矛盾。
4.【强制】在描述清晰的前提下尽量简洁,对于某些可能造成不理解的词给出解释(例如出题人定义的新概念、回文、子序列、子串、异或等等),题目背景不能干扰对题意的理解。【建议】题目背景单开一段,不与题目正文杂糅。
5.【建议】一般来讲,对于初始值、边界值的说明放在题目正文,对测试数据规模的说明放在输入描述,对输出格式的说明放在输出描述,对样例整体的解释说明(例如解释得到输出的过程、存在多种情况输出XXX也为正确答案等等)放在补充说明。当然可以根据实际情况做适当调整。
- 建议参考codeforces命题规范:https://docs.google.com/document/d/e/2PACX-1vRhazTXxSdj7JEIC7dp-nOWcUFiY8bXi9lLju-k6vVMKf4IiBmweJoOAMI-ZEZxatXF08I9wMOQpMqC/pub
测试数据相关
1.【强制】首先,保证测试数据的健壮性(Robust,又称鲁棒性)。我们在生成数据的时候尽可能考虑所有边界条件 (例如全0全1,顺序逆序,线图,菊花图,网格图,完全图,树等等,适当安排测试数据规模)。
- 数据规模推荐以10的幂为一个单位,每个单位5-10组,例如对于数据范围1e5的题,可以按照以下单位来生成1-10,10-100,100-1000,1000-10000,10000-100000,100000-100000。
2.【建议】为了适当提升判题速度,测试数据在保证健壮的前提下尽可能少。
3.【建议】O(1)或O(logn)算法强烈推荐多组样例。
4.【强制】输出浮点数应当规定精度,然后使用特判。
其他
1.【强制】不出过于经典的复杂套路题。
- 注意要同时满足“过于经典”、“复杂套路”两个条件,例如国王游戏。
2.【建议】少出或不出交互题。
3.【建议】代码量非常大的题不超过1道。
4.【建议】易,中下,中,中上,难题比例推荐为2:3:4:3:1。
5.【强制】思维难度占主要部分。对于复杂算法和复杂数据结构,不出裸题。
6.【强制】不出近期codeforces、牛客等网站上的出现过比赛题。但可以进行改编,要保证改编前后解题思路不同。
7.【建议】热身赛易,中,难比例推荐3:2:1。
8.【强制】代码量大的题不能通过强行增加复杂的条件实现。
- 一些工程题,或自然的模拟题是可以出现的,例如猪国杀。
9.【建议】对于本规则没有涉及到的地方,或遇到某些特殊情况,可按照常识判断。
秋季赛
1.【强制】涉及复杂算法和数据结构的题只出1道(压轴题)或不出。
2.【强制】准备两个签到,一个签到题直接输出即可AC,另一个if-else,直接排序(允许$n^2$)、简单贪心、枚举等。
3.【建议】中档题多为枚举、简单搜索、简单贪心、可以简单实现的数学。中上等题可以出现入门dp、二分等基础算法。
4.【建议】可以在提问中解释部分可能不理解的专业术语(例如取模)。
5.【建议】热身赛推荐出一些熟悉评测系统使用的题目(例如熟悉提问功能,认识long long等等)。
春季赛
1.【强制】签到题为if-else,输入输出思维,直接排序(允许$n^2$)、简单贪心、枚举等可以直接实现的题目,准备两个签到。
2.【建议】中下等和中等题仅涉及基础算法和简单数学,由思维难度作区分。
3.【建议】中上等和压轴题可涉及高级算法和数据结构。
参考:
-
OI-wiki:https://oi-wiki.org/contest/problemsetting/
-
codeforces命题标准:https://docs.google.com/document/d/e/2PACX-1vRhazTXxSdj7JEIC7dp-nOWcUFiY8bXi9lLju-k6vVMKf4IiBmweJoOAMI-ZEZxatXF08I9wMOQpMqC/pub