Special Judge(特判程序)在OnlineJudge(在线判题系统)中的使用方法

3 篇文章 0 订阅
2 篇文章 0 订阅

引言

最近搭了一个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
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值