引言
最近搭了一个OnlineJudge,在一些比赛中,为了避免选手骗过测试,所以一个比较完善的OJ测试必须加上Special Judge
例如:题目要求:求a+b的值
测试用例:
输入1 2
输出:3
输入5 8
输出:13
输入4 5
输出:9
大家肯定都知道,正确的姿势是这样的:
#include"stdio.h"
int main()
{
int a,b,c;
scanf("%d %d",&a,&b);
c=a+b;
printf("%d",c);
}
但是有的同学很聪明,知道系统有BUG,写了一个骗过测试的写法:
#include"stdio.h"
int main()
{
int a,b;
int c=3;
int d=13;
int e=9;
scanf("%d %d",&a,&b);
if(a==1 && b==2)
{
printf("%d",c);
}
if(a==5 && b==8)
{
printf("%d",d);
}
if(a==4 && b==5)
{
printf("%d",e);
}
}
可以看出,这样的答案也能通过测试,也能拿到满分,如果这样的话,那这个测评系统毫无意义,比赛也没有意义,因为不够“聪明”。
假如不加入Special Judge,有的选手为了得分,会巧妙的利用测试用例
一.什么是Special Judge
Special Judge(最准确的中文翻译应该是特判程序)是指OJ将使用一个特定的程序来**判断提交的程序的输出是不是正确的,而不是单纯地看提交的程序的输出是否和标准输出一模一样。
二.Special Judge的使用场景
通常的ACM题目包括以下几项内容:题目描述(Description)、输入描述(Input)、输出描述(Output)、样例输入(Sample Input)、样例输出(Sample Out),在后台则包括测试输入(Input Data)和测试输出(Output Data)两项。在评测用户提交的程序正确与否时,系统会将样例输入和测试输入重定向作为程序的标准输入,通过判断程序对应的输出是否与期待的输出完全相同,来判断解答是否正确。
对于同一道题目,用户可能使用各种不同的方法来解答,所以对于某些特殊的题目,其结果可能不唯一,但都符合题目要求。此类题目就需要进行特判(Special Judge)。很多开源的OJ系统都提供了特判功能。
举个例子:
题目最终要求输出一个解决方案,而且这个解决方案可能不唯一。
比如题目最终要求输出一个浮点数,而且会告诉只要答案和标准答案相差不超过某个较小的数就可以,比如0.01。这种情况保留3位小数、4位小数等等都是可以的,而且多保留几位小数也没什么坏处。
三.OnlineJudge手把手带你在写一个简单的测评
首先,我们要了解OJ系统的沙箱测评机制:
#include <stdio.h>
#define AC 0
#define WA 1
#define ERROR -1
int spj(FILE *input, FILE *user_output);
void close_file(FILE *f){
if(f != NULL){
fclose(f);
}
}
int main(int argc, char *args[]){
FILE *input = NULL, *user_output = NULL;
int result;
if(argc != 3){
printf("Usage: spj x.in x.out\n");
return ERROR;
}
input = fopen(args[1], "r");
user_output = fopen(args[2], "r");
if(input == NULL || user_output == NULL){
printf("Failed to open output file\n");
close_file(input);
close_file(user_output);
return ERROR;
}
result = spj(input, user_output);
printf("result: %d\n", result);
close_file(input);
close_file(user_output);
return result;
}
int spj(FILE *input, FILE *user_output){
/*
parameter:
- input,标程输入的文件指针
- user_output,用户输出文件的指针
return:
- 如果用户答案正确,返回AC
- 如果用户答案错误返回WA
- 如果主动捕获到自己的错误,如内存分配失败,返回ERROR
请用户完成此函数.
demo:
int a, b;
while(fscanf(f, "%d %d", &a, &b) != EOF){
if(a -b != 3){
return WA;
}
}
return AC;
*/
}
看文档大概是这样子的,无非就是文件指针生成,读取,这些,当然,这个是一个可在本地完整运行的脚本测评代码,在线测评系统一般都封装好了生成文件等这些函数的操作
所以,我们如何用呢?
在一些比赛中,为了避免选手骗过测试,
例子一:给出一个不小于12的正整数n,请你输出两个合数,使他们的和等于n
首先在自己搭好的OJ系统上创建一个题目:计算两数之和
一般完成输入,输出的一些模板创建,然后我们不用导入文件,选择一个全新的自动化测评方式,Special Judge:
#include <stdio.h>
int sum(int x, int y)
{
return x + y;
}
int main()
{
int a, b;
while(scanf("%d%d", &a, &b) != EOF)
printf("%d\n", sum(a, b));
return 0;
}
例子二:给出一个不小于12的正整数n,请你输出两个合数,使他们的和等于n
再详细一些:
首先分别制定1.in,2.in,3.in,4.in,(测试输入)、1.out,2.out,3.out,4.out(测试输出)如下:
上传测试文件,编写spj.cc(特判程序):
#include <stdio.h>
int prime(int a)
{
int i;
for(i=2;i<a;i++)
if(a%i==0)
return 0;//a%i==0不成立
else
return 1;//a%i==0成立
}
bool is_prime(int x)//判断素数,伪代码
{
if(x==prime(1))
return true;
else
return false;
}
int main(int argc,char *args[])//主函数
{
FILE * f_in=fopen(args[1],“r”);//测试输入
FILE * f_out=fopen(args[2],“r”);//测试输出
FILE * f_user=fopen(args[3],“r”);//用户输出
int ret=0;//返回值
int T,n,a,b;
fscanf(f_in,“%d”,&T);//从输入中读取数据组数T
while(T–)
{
fscanf(f_in,“%d”,&n);
fscanf(f_user,“%d%d”,&a,&b);
if(a+b!=n || is_prime(a) || is_prime(b))
ret = 1;//Wrong Answer
}
fclose(f_in);
fclose(f_out);
fclose(f_user);
return ret;
}
三.关于我
学校 | 昆明理工大学津桥学院软件工程大四学生 |
---|---|
目前在 | Asiainfo CUC Java后端实习生 |
开源社区 | 飞桨开发者专家(PPDE) |
格言 | 以前不懂事,现在只想搞钱 |
爱好 | 除了瞎搞,没啥爱好了, |
个人网站 | http:leceshi.cn |