• 浅谈spj


    SPJ(special judge)是个好玩的东西,毕竟各类神奇的题目SPJ经常作为救火工具(比如说一不小心出成验证类的题目)。

    但SPJ是个坑,毕竟只让用个“testlib.h”,输入还特别奇怪。今天我就带大家来玩一玩这个奇怪的东西

    写在前面:

    本来已经退役了暂时不打算补坑,但今天遇上了静静(我们教练)让我教她SPJ,于是索性把这篇文章的坑补完

    一、明确内容

    SPJ总体上主要分两大类:精度判断(lemon上叫实数比较)和方案验证。这两个东西其实思路完全不是一码事。

    不过由于都是基于SPJ比较的大框架,所以都被归入SPJ。在开动之前我们必须明确你到底要写哪个,不然可能绕来绕去费上老半天劲儿还得返工。

    二、操作简介(以下内容部分来自luogu的SPJ帮助,但是有部分修改,请小心食用)

    1.读入部分

    void registerTestlibCmd(argc, argv)

    初始化checker,必须在最前面调用一次。

    char a=readChar()

    读入一个char,指针后移一位。

    char a=readChar(char c)

    和上面一样,但是只能读到一个字母c

    char a=readSpace()

    同 readChar(' ').

    string a=readToken()

    读入一个字符串,但是遇到空格、换行、eof为止、

    long long a=readLong()

    读入一个longlong/int64

    long long a=readLong(long long L, long long R)

    同上,但是限定范围(包括L,R)

    int a=readInt()

    读入一个int

    int a=readInt(int L, int R),

    同上,但是限定范围(包括L,R)`

    double a=readReal()

    读入一个实数

    double a=readReal(double L, double R),

    同上,但是限定范围(包括L,R)

    double a=readStrictReal(double L, double R, int minPrecision, int maxPrecision),

    读入一个限定范围精度位数的实数。

    string a=readString(),
    string a=readLine()

    读入一行string,到换行或者eof为止

    void readEoln()

    读入一个换行符

    void readEof()

    读入一个eof

    int a=eof()

    2.输出部分

    给出AC

    quitf(\_ok, "The answer is correct. answer is %d", ans);

    给出WA

    quitf(\_wa, "The answer is wrong: expected = %f, found = %f", jans, pans);

    给出PC(Partially Correct),并且可以获得该点50%的分数

    quitp(0.5,"Partially Correct get %d percent", 50);

    步骤分解:

    关于输入:

    输入部分没有什么好强调的,写过快读的同学都知道正确的写法。定义一个变量等于输入函数返回的值即可。

    但需要注意的是,SPJ不是一个普通的C++程序,他使用的库是

    #include“testlib.h”

    所以,C++的很多操作在SPJ里是不能使用的。典型代表就是输入输出。你能且仅能使用上面提供的的读入方式。"cin",“printf”之类的东西都会让你CE

    关于输出:

    一句话说不清,各位请看下图

     AC版

    引号里的内容等价于上图的黑底白字部分

    WA版

    PC版(这个与上面的稍有不同)

    三、实例分析

    精度判断

    精度判断相对于后者很简单,因为精度判断其实基本与常规评测模式并无差异。我们通常只需要判断选手答案与正确答案的差异是否在精度范围内。

    所以使用SPJ的方式读入标准答案文件与选手输出文件,进行进度比较即可。

    实战样例:

    luoguP2164

    这道题的SPJ属于典型的精度比较类

    因为保留一位小数,所以存在向下,向上,四舍五入等多种保留方式(题目中并没有规定)

    所以我们考虑规定误差为0.1,直接误差比较即可

    实例代码:

    #include"testlib.h"//专属头文件不可少
    using namespace std;
    #define rii register int i 
    double eps=0.1;
    int n,m;
    int main(int argc,char *argv[])//流文件操作莫忘掉
    {
        registerTestlibCmd(argc,argv);//初始化checker要记牢
        n=inf.readInt();
        m=inf.readInt();
        for(rii=1;i<=m;i++)
        {
            double a1=ans.readDouble();
            double a2=ouf.readDouble();
            if(abs(a1-a2)>eps)//貌似cmath库直接集成,不需要自己写
            {
                quitf(_wa,"wrong answer on line %d",i);//给出错误结果
            }
        }
        quitf(_ok,"correct answer");//给出正确结果
        return 0;
    }

     luoguP3516

    这是一个典型的验证类SPJ

    给定一个方案,让你检验是否能达到要求的目的

    两类操作:

    (a) 将最后一个数移到最前面

    (b) 把第三个数移到最前面

    对于每个操作,我们简单的写一个双向链表维护即可

    然后对于完成所有操作的序列,我们需要进行一次比较

    判断得出的序列与给定的标准序列是否相同

    然后按位比较即可

    具体操作详见代码中的注释(以码风为界,分别为我和苏卿念写的【哪个是谁靠自己猜吧】)

    #include "testlib.h"
    #include <ctime> 
    using namespace std;
    struct lb{
        int pre,nxt,val;
    }x[2005];
    int n,cnt,head,tail;
    void ltof(int num)//一号双向链表操作,把末尾的元素移到头部
    {
        while(num--)
        {
            int ls=x[tail].pre;
            x[ls].nxt=0;
            x[tail].nxt=head;
            x[tail].pre=0;
            x[head].pre=tail;
            head=tail;
            tail=ls;
        }
    }
    void ttof(int num)//二号双向链表操作:第三个移到第一个
    {
        while(num--)
        {
            int cnt=0;
            int ls=head;
            for(int i=1;i<=2;i++)
            {
                ls=x[ls].nxt;
            }
            int forth=x[ls].nxt;
            int kkk=x[ls].pre;
            x[forth].pre=kkk;
            x[kkk].nxt=x[ls].nxt;
            x[ls].pre=0;
            x[ls].nxt=head;
            x[head].pre=ls;
            head=ls;
        }
    }
    void change(int num,int cz){//此函数用于判断操作
        if(cz==0) ltof(num);
        else ttof(num);
    }
    char opt;int now;
    char pos;
    int m;
    int main(int argc,char *argv[]) {//真正的spj主程序开始了
        registerTestlibCmd(argc,argv);、、初始化输入
        n=inf.readInt();
        for(int i=1;i<=n;i++) x[i].val=inf.readInt(),x[i].pre=i-1,x[i].nxt=i+1;//输入原始序列,并构造链表
        head=1,tail=n;int qnt=1;
        opt=ans.readChar();//读入操作类型
        if(opt=='N') {//判断操作类型
            pos=ouf.readChar();//读入操作数
            if(pos!='N') quitf(_wa,"expect NIE found %c.score:pqp",pos);
            pos=ouf.readChar();
            if(pos!='I') quitf(_wa,"expect IE found %c.score:pqp",pos);
            pos=ouf.readChar();
            if(pos!='E') quitf(_wa,"expect E found %c.score:pqp",pos);
            pos=ouf.readChar();
            if(pos=='
    '||pos==3||pos==26) quitf(_ok,"the answer ios corect .score:qaq");
            if(pos!=' ') quitf(_wa,"expect  found %c.score:pqp",pos);
            pos=ouf.readChar();
            if(pos!='D') quitf(_wa,"expect DA found %c.score:pqp",pos);
            pos=ouf.readChar();
            if(pos!='A') quitf(_wa,"expect A found %c.score:pqp",pos);
            pos=ouf.readChar();
            if(pos!=' ') quitf(_wa,"expect   found %c.score:pqp",pos);
            pos=ouf.readChar();
            if(pos!='S') quitf(_wa,"expect SIE found %c.score:pqp",pos);
            pos=ouf.readChar();
            if(pos!='I') quitf(_wa,"expect IE found %c.score:pqp",pos);
            pos=ouf.readChar();
            if(pos!='E') quitf(_wa,"expect E found %c.score:pqp",pos);
            quitf(_ok,"the answer ios corect .score:qaq");
        }
        m=ouf.readInt();
        
        if(n==1&&m==0) {quitf(_ok,"correct answer");return 0;}
        else if(n==1) {quitf(_wa,"wrong answer");return 0;}
        for(int i=1;i<=m;i++){
            pos=0;now=0;
            pos=ouf.readChar();
            while(pos>'9'||pos<'0') pos=ouf.readChar();
            while(pos<='9'&&pos>='0') now=now*10+pos-'0',pos=ouf.readChar();
            if(now<=0||now>n) quitf(_wa,"wrong output on operation %d,found %d,expect in [ 1 , %d ] .score:vov",i,now,n);
            if(pos=='b') now%=3;
            if(pos=='a') now%=n; 
            change(now,pos=='b');
            
        }
        int wz=head;
        while(wz!=tail){
            int nxt=x[wz].nxt;
            if(x[nxt].val<x[wz].val){
                quitf(_wa,"wrong answer");
                return 0;
            }
            wz=nxt;
        }
        if(x[x[tail].pre].val>x[tail].val) quitf(_wa,"worong answer ");
        else quitf(_ok,"correct answer ");
        return 0;
    }

    未完待续……

  • 相关阅读:
    第四次作业
    第三次
    第十次作业
    第九次作业
    第八次作业
    10.29第七次
    15
    14
    13 this
    12 电视机
  • 原文地址:https://www.cnblogs.com/ztz11/p/10657351.html
Copyright © 2020-2023  润新知