oi-wiki
Version:
wiki for OI / ACM-ICPC
224 lines (195 loc) • 6.3 kB
Markdown
一道题如果有多组解,我们就需要一个程序来判断答案合法性,这便是 Special Judge (spj),又常被称作 checker,下面介绍部分评测工具 /OJ 的 spj 编写方法。
???+ warning
spj 还应当判断文件尾是否有多余内容,及输出格式是否正确(如题目要求数字间用一个空格隔开,而选手却使用了换行)。但是,目前前者只有 Testlib 可以方便地做到这一点,而后者几乎无人去特意进行这种判断。
浮点数时应注意 nan,不合理的判断方式会导致输出 nan 即可 AC。
以下均使用 C++,以 “要求标准答案与选手答案差值小于 1e-3,文件名为 num” 为例。
## Testlib
[Testlib](https://codeforces.com/testlib) 是一个强大的算法竞赛题目辅助系统,只需要在程序中引入 [testlib.h](https://github.com/MikeMirzayanov/testlib/blob/master/testlib.h) 头文件即可使用。用法详见 [Testlib](/intro/testlib) 页面。
使用 Testlib 编写 spj 的好处为我们不再需要判断文件尾的多余内容,其会帮助我们自动判断,也无需担忧 nan。
必须使用 Testlib 做 spj 的 评测工具 /OJ:Codeforces、洛谷、UOJ 等
可以使用 Testlib 做 spj 的 评测工具 /OJ:LibreOJ(SYZOJ 2)、Lemon 等
SYZOJ 2 所需的修改版 Testlib 可以在[这里](https://pastebin.com/3GANXMG7)获取到,感谢 [cyand1317](https://loj.ac/article/124)。
Lemon 所需的修改版 Testlib 可以在[这里](https://paste.ubuntu.com/p/JsTspHHnmB/)获取到,感谢 matthew99。注意此版本 Testlib 注册 checker 应使用 `registerLemonChecker()` 而非 `registerTestlibCmd()`。
其他评测工具 /OJ 大部分需要按照其 spj 编写格式修改 Testlib。
```cpp
int main(int argc, char *argv[]){
/*
* inf:输入
* ouf:选手输出
* ans:标准输出
*/
registerTestlibCmd(argc, argv);
double pans=ouf.readDouble(), jans=ans.readDouble();
if(abs(pans - jans) < 1e-3) quitf(_ok, "Good job");
else quitf(_wa, "Too big or too small, expected %f, found %f", jans, pans);
}
```
## Lemon
**Lemon 有现成的修改版 Testlib,建议使用 Testlib,见 [Testlib](
```cpp
int main(int argc,char *argv[]){
/*
* argv[1]:输入
* argv[2]:选手输出
* argv[3]:标准输出
* argv[4]:单个测试点分值
* argv[5]:输出最终得分
* argv[6]:输出错误报告
*/
FILE* fin = fopen(argv[1], "r");
FILE* fout = fopen(argv[2], "r");
FILE* fstd = fopen(argv[3], "r");
FILE* fscore = fopen(argv[5], "w");
FILE* freport = fopen(argv[6], "w");
double pans, jans;
fscanf(fout, "%lf", &pans);
fscanf(fstd, "%lf", &jans);
if(abs(pans - jans) < 1e-3){
fprintf(fscore, "%s", argv[4]);
fprintf(freport, "Good job");
}else{
fprintf(fscore, "%d", 0);
fprintf(freport, "Too big or too small, expected %f, found %f", jans, pans);
}
}
```
## Cena
```cpp
int main(int argc,char *argv[]){
/*
* FILENAME.in:输入
* FILENAME.out:选手输出
* argv[2]:标准输出
* argv[1]:单个测试点分值
* score.log:输出最终得分
* report.log:输出错误报告
*/
FILE* fin = fopen("num.in", "r");
FILE* fout = fopen("num.out", "r");
FILE* fstd = fopen(argv[2], "r");
FILE* fscore = fopen("score.log", "w");
FILE* freport = fopen("report.log", "w");
double pans, jans;
fscanf(fout, "%lf", &pans);
fscanf(fstd, "%lf", &jans);
if(abs(pans - jans) < 1e-3){
fprintf(fscore, "%s", argv[1]);
fprintf(freport, "Good job");
}else{
fprintf(fscore, "%d", 0);
fprintf(freport, "Too big or too small, expected %f, found %f", jans, pans);
}
}
```
## CCR
```cpp
int main(int argc,char *argv[]){
/*
* stdin:输入
* argv[3]:选手输出
* argv[2]:标准输出
* stdout:L1:输出最终得分比率
* stdout:L2:输出错误报告
*/
FILE* fout = fopen(argv[3], "r");
FILE* fstd = fopen(argv[2], "r");
double pans, jans;
fscanf(fout, "%lf", &pans);
fscanf(fstd, "%lf", &jans);
if(abs(pans - jans) < 1e-3){
printf("%d\n", 1);
printf("Good job");
}else{
printf("%d\n", 0);
printf("Too big or too small, expected %f, found %f", jans, pans);
}
}
```
## Arbiter
## HUSTOJ
```cpp
int main(int argc,char *argv[]){
/*
* argv[1]:输入
* argv[3]:选手输出
* argv[2]:标准输出
* exit code:返回判断结果
*/
FILE* fout = fopen(argv[3], "r");
FILE* fstd = fopen(argv[2], "r");
double pans, jans;
fscanf(fout, "%lf", &pans);
fscanf(fstd, "%lf", &jans);
if(abs(pans - jans) < 1e-3) return AC;
else return WA;
}
```
## QDUOJ
QDUOJ 就麻烦一点,因为它的带 spj 的题目没有标准输出,只能把 std 写进 spj 跑出标准输出再判断。
```cpp
double solve(...){
// std
}
int main(int argc,char *argv[]){
/*
* argv[1]:输入
* argv[2]:选手输出
* exit code:返回判断结果
*/
FILE* fin = fopen(argv[1], "r");
FILE* fout = fopen(argv[2], "r");
//读入
double pans, jans;
fscanf(fout, "%lf", &pans);
jans = solve(...);
if(abs(pans - jans) < 1e-3) return AC;
else return WA;
}
```
## LibreOJ(SYZOJ 2)
**LibreOJ(SYZOJ 2) 有现成的修改版 Testlib,建议使用 Testlib,见 [Testlib](
```cpp
int main(int argc,char *argv[]){
/*
* in:输入
* user_out:选手输出
* answer:标准输出
* code:选手代码
* stdout:输出最终得分
* stderr:输出错误报告
*/
FILE* fin = fopen("in", "r");
FILE* fout = fopen("user_out", "r");
FILE* fstd = fopen("answer", "r");
FILE* fcode = fopen("code", "r");
double pans, jans;
fscanf(fout, "%lf", &pans);
fscanf(fstd, "%lf", &jans);
if(abs(pans - jans) < 1e-3){
printf("%d", 100);
fprintf(stderr, "Good job");
}else{
printf("%d", 0);
fprintf(stderr, "Too big or too small, expected %f, found %f", jans, pans);
}
}
```