浅谈spj
SPJ(special judge)是個好玩的東西,畢竟各類神奇的題目SPJ經常作為救火工具(比如說一不小心出成驗證類的題目)。
但SPJ是個坑,畢竟只讓用個“testlib.h”,輸入還特別奇怪。今天我就帶大家來玩一玩這個奇怪的東西
寫在前面:
本來已經退役了暫時不打算補坑,但今天遇上了靜靜(我們教練)讓我教她SPJ,于是索性把這篇文章的坑補完
一、明確內容
SPJ總體上主要分兩大類:精度判斷(lemon上叫實數(shù)比較)和方案驗證。這兩個東西其實思路完全不是一碼事。
不過由于都是基于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()
讀入一個實數(shù)
double a=readReal(double L, double R),
同上,但是限定范圍(包括L,R)
double a=readStrictReal(double L, double R, int minPrecision, int maxPrecision),
讀入一個限定范圍精度位數(shù)的實數(shù)。
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%的分數(shù)
quitp(0.5,"Partially Correct get %d percent", 50);
步驟分解:
關于輸入:
輸入部分沒有什么好強調的,寫過快讀的同學都知道正確的寫法。定義一個變量等于輸入函數(shù)返回的值即可。
但需要注意的是,SPJ不是一個普通的C++程序,他使用的庫是
#include“testlib.h”
所以,C++的很多操作在SPJ里是不能使用的。典型代表就是輸入輸出。你能且僅能使用上面提供的的讀入方式。"cin",“printf”之類的東西都會讓你CE
關于輸出:
一句話說不清,各位請看下圖
AC版
引號里的內容等價于上圖的黑底白字部分
WA版
PC版(這個與上面的稍有不同)
三、實例分析
精度判斷
精度判斷相對于后者很簡單,因為精度判斷其實基本與常規(guī)評測模式并無差異。我們通常只需要判斷選手答案與正確答案的差異是否在精度范圍內。
所以使用SPJ的方式讀入標準答案文件與選手輸出文件,進行進度比較即可。
實戰(zhàn)樣例:
luoguP2164
這道題的SPJ屬于典型的精度比較類
因為保留一位小數(shù),所以存在向下,向上,四舍五入等多種保留方式(題目中并沒有規(guī)定)
所以我們考慮規(guī)定誤差為0.1,直接誤差比較即可
實例代碼:
#include"testlib.h"http://專屬頭文件不可少
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) 將最后一個數(shù)移到最前面
(b) 把第三個數(shù)移到最前面
對于每個操作,我們簡單的寫一個雙向鏈表維護即可
然后對于完成所有操作的序列,我們需要進行一次比較
判斷得出的序列與給定的標準序列是否相同
然后按位比較即可
具體操作詳見代碼中的注釋(以碼風為界,分別為我和蘇卿念寫的【哪個是誰靠自己猜吧】)
#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){//此函數(shù)用于判斷操作
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();//讀入操作數(shù)
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;
}
未完待續(xù)……
總結
- 上一篇: bugku web所有writeup_超
- 下一篇: 女网名简单大方有内涵67个