如何給LOJ補全special judge

首先你要會寫一個叫$data.yml$的東西,java

這裏面記錄了這道題的$subtask$計分策略node

也告訴了評測姬這道題是提交答案仍是$spj$仍是交互題python

那麼,$YAML$語言是啥啊?ios

別問我,我也不會 本着會用能用就行的原則數組

給你們講講$LOJ$的$special\ jidge$怎麼用ruby

$yaml$裏基本上的所有語法:dom

$Structure$經過空格來展現。$Sequence$裏的項用$-$來表明,Map裏的鍵值對用$:$分隔.函數

在$LOJ$咱們須要寫一份相似這樣的$data.yml$文件:測試

(摘自 LOJ幫助 ,稍有改動)ui

subtasks:           # 定義subtask
  - score: 30              # 這個子任務的分數(注意,全部子任務的總分必須爲 100)
    type: sum              # 子任務類型,可選的值有 sum、min 和 mul.
    cases: [1, 2, 3]       # 測試點編號可爲數字
  - score: 30              # 另外一個子任務
    type: mul        #sum:求和 mul:求積以後摺合回100 min:取最小值按百分比摺合 
    cases: ['4', '5', '6'] # 測試點編號也可爲字符串
inputFile: 'dat#.in' # 告訴評測姬你測試數據包中的輸入文件 outputFile: 'dat#.ans' # 告訴評測姬你測試數據包中的輸出文件 # 上述文件名中的 '#' 字符將被替換爲測試點編號,eg:dat1.in,dat1.out # Special Judge 可省略 specialJudge: language: cpp      #語言,這裏用簡稱  fileName: spj.cpp    #這裏是你 答案檢查器 的名子,後綴最好要和language同樣(沒試過不同行不行)

語言簡稱:ccppcpp11csharphaskelljavalualuajitnodejspascalpython2python3rubyvalavbnetocam

這裏開始舉個栗子,$A+B\ problem$ ,$10$個測試點,爲$data1.in,data1.out \~ data10.in,data10.out$。

而後

  對於30%的數據,a,b<=100;

  對於另30%的數據,a,b<=1000000;

  對於另40%的數據,a,b<=100000000000000000000000000000000000000000000000000000000000000000;

 這時候,你的$data.yml$裏就要這樣寫:

subtasks:
  - score: 30
    type: sum
    cases: [1, 2, 3]
  - score: 30
    type: mul
    cases: [4, 5, 6]
  - score: 40
    cases: [7, 8, 9, 10]

inputFile: 'data#.in'
outputFile:  'data#.out'

而後對於某沙華大佬的那道題,須要有$spj$

subtasks:
  - score: 12
    type: sum
    cases: ['01-14','02-1234','03-1234','04-14','05-1234','06-1234','07-1234','08-1234','09-14','10-1234','11-134','12p-1234','23p-1234']
  - score: 15
    type: sum
    cases: ['02-1234','03-1234','05-1234','06-1234','07-1234','08-1234','10-1234','12p-1234','13-234','14-234','15-234','16-234','17-234','18-234','19-234','20-234','21-234','22-234','23p-1234','24-234','25-234','26-234','27-234','28-234','29-234','30-234','31-234','32-234','33-234','34-234']
  - score: 23
    type: sum
    cases: ['02-1234','03-1234','05-1234','06-1234','07-1234','08-1234','10-1234','11-134','12p-1234','13-234','14-234','15-234','16-234','17-234','18-234','19-234','20-234','21-234','22-234','23p-1234','24-234','25-234','26-234','27-234','28-234','29-234','30-234','31-234','32-234','33-234','34-234','35-34','36-34','37-34','38-34','39-34','40-34','41-34','42-34']
  - score: 50
    type: sum
    cases: ['01-14','02-1234','03-1234','04-14','05-1234','06-1234','07-1234','08-1234','09-14','10-1234','11-134','12p-1234','13-234','14-234','15-234','16-234','17-234','18-234','19-234','20-234','21-234','22-234','23p-1234','24-234','25-234','26-234','27-234','28-234','29-234','30-234','31-234','32-234','33-234','34-234','35-34','36-34','37-34','38-34','39-34','40-34','41-34','42-34','43-4','44-4','45-4','46-4','47-4','48-4','49-4','50-4','51-4','52-4','53-4','54-4','55-4','56-4','57-4','58-4','59-4','60-4','61-4','62-4','63-4','64-4','65-4','66-4']

inputFile: 'demarcation.#.in'
outputFile: 'demarcation.#.sol'

specialJudge:
  - language: cpp11
    fileName: spj_cpp11.cpp
 - language: cpp
   fileName: checker.cpp

(嚶嚶嚶這個數據點編號我也是醉了qwq)

下面開始講講怎麼寫$special\ judge$

只會C++

$Special\ Judge$ 程序運行時,其目錄下會有四個文件 inputuser_outanswercode,分別對應該測試點的輸入文件、用戶輸出、該測試點的輸出文件、用戶的代碼(對於非提交答案題目)。

你能夠從這四個文件裏讀入東西……

$Special\ Judge$ 程序運行完成後,應將該測試點的得分輸出到標準輸出(stdout)中(範圍爲 0100,將自動摺合爲測試點分數),並將提供給用戶的額外信息輸出到標準錯誤輸出(stderr)中。

而後好像……

LOJ並不支持 $testlib.h$

$mdzz$

(本寶寶心裏是拒絕給一個沒$testlib.h$的OJ寫$spj$的)

既然不支持,因爲今天主要講的是$LOJ$的$spj$寫法

那我哭着也要寫完吧……

上面說了,咱們能夠對那4個文件幹一些事情

 因此我這裏只介紹輸入輸出怎麼整,並不介紹運算(由於判斷合法性要相對於題而言,並不能歸納)

void read(char *fileName)
{
    FILE *f = fopen(fileName, "r");
    fscanf(f,...,...);
    fclose(f);
}

 調用這個函數的意思是:

從名爲$fileName$的文件裏讀取一些東西

其中,

$fscanf(  )$裏面先要有個$f,$以後就是$scanf$的格式。

以後就是給出正確、錯誤狀態以及得分

void writ(const char s[],int point) {
//給出答案錯誤以及得分
/*
根據LOJ幫助裏說,得分爲stdout,詳情爲stderr
因而就用fprintf()來輸出stderr,printf來輸出得分
參數中,char數組表示的是詳細信息的一個字符串,point是得分
*/
    fprintf(stderr, "%s\n", s);
    printf("%d\n",point);
    exit(0);
}

而後就基礎語法了

放上一波頭文件

#include<cstdio>
#include<fstream>
#include<cstdlib>
#include<vector>
#include<cstring>
#include<cctype>
#include<algorithm>
using namespace std;

若是你不會寫$data.yml$,但又想要$spj$,你須要在文件裏包含一個 spj_***.xxx

其中,$***$爲語言簡稱,$xxx$爲任意後綴(強烈安利$.qwq$文件)

下面給出$a+b\ problem$的栗子:

#include<cstdio>
#include<fstream>
#include<cstdlib>
#include<vector>
#include<string>
#include<cctype>
#include<algorithm>
using namespace std;
int std_ans;
int per_out;
void read_ans(char *fileName)//從文件裏讀入一個數,存到std_ans裏 
{
    FILE *f = fopen(fileName, "r");
    fscanf(f,"%d",&std_ans);
    fclose(f);
}
void read_out(char *fileName)//從文件裏讀入一個數,存到per_out裏 
{
    FILE *f = fopen(fileName, "r");
    fscanf(f,"%d",&per_out);
    fclose(f);
}
void writ(const char s[],int point) {
    fprintf(stderr, "%s\n", s);
    printf("%d\n",point);
    exit(0);
}
int main()
{
    read_out("user_out");//從user_out,用戶輸出中讀取per_out 
    read_ans("answer");//從 該測試點的輸出文件 中讀取 std_ans
    if(std_ans==per_out) writ("ok the answer is corrit",100);//返回正確
    else writ("you are wrong",0);//返回錯誤 
} 

到這裏,基礎教程就沒了,高級的奇技淫巧還在後面……


你有沒有發現,若是你要讀一堆東西,發現你要寫好多$read()$函數……

因而就崩潰了啊。

那怎麼辦呢?

魔改一下就好啊

int read_int(char *fileName)//從文件裏讀入一個數,返回這個數的值 
{
    FILE *f = fopen(fileName, "r");
    int now;
    fscanf(f,"%d",&now);
    fclose(f);
    return now;
}

這樣咱們就少寫了好多東西哎

此時的$a+b$少了一個函數:

#include<cstdio>
#include<fstream>
#include<cstdlib>
#include<vector>
#include<string>
#include<cctype>
#include<algorithm>
using namespace std;
int std_ans;
int per_out;
int read_int(char *fileName)
{
    FILE *f = fopen(fileName, "r");
    int now;
    fscanf(f,"%d",&now);
    fclose(f);
    return now;
}
void writ(const char s[],int point) {
    fprintf(stderr, "%s\n", s);
    printf("%d\n",point);
    exit(0);
}
int main()
{
    per_out=read_int("user_out");
    std_ans=read_int("answer");
    if(std_ans==per_out) writ("ok the answer is corrit",100);
    else writ("you are wrong",0);
} 

 

哇……這樣咱們是否是能夠類比呢?

沒錯

char read_char(char *fileName)
{
    FILE *f = fopen(fileName, "r");
    char now;
    fscanf(f,"%c",&now);
    fclose(f);
    return now;
}

固然,下面這個慎用……

下面這個慎用!!!

前面加 $#include<iostream>$

string resd_string(char *fileName)
{
    freopen(fileName,"r",stdin);
    string now;
    cin>>now;
    fclose(stdin);
    return now;
}

 這樣是好寫了……可是別忘了

$checker.cpp$運行也是有時間限制的啊啊啊、

因此……

你讀一個數運行一次$fopen,fclose$可能會崩

因此你能夠寫一個指定從某文件讀取$int,char$的函數。

這裏我就不寫了$qwq$


 哇啊啊啊神tm LOJ支持$testlib.h$

那麼就來說講$testlib.h$怎麼用吧

首先咱們須要有這個$testlib.h$

下載地址:戳這裏

而後呢,咱們須要的是

使用自定義校驗器,請在problem.conf中去掉use_builtin_checker那一行。

(其實沒什麼用)

開始介紹$testlib.h$中的語法

首先必要的幾個東西:

#include "testlib.h"
int main(int argc,char *argv[])
{
    registerTestlibCmd(argc, argv);
   /*    
    your main function
    */ 
    return 0;
}

第四行那句話是必需要加的,所謂的初始化$qwq$

不過,偷偷的告訴你,$testlib.h$裏面包含的頭文件:

#define random __random_deprecated
#include <stdlib.h>
#include <cstdlib>
#include <climits>
#include <algorithm>
#undef random

#include <cstdio>
#include <cctype>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <cmath>
#include <sstream>
#include <fstream>
#include <cstring>
#include <limits>
#include <stdarg.h>
#include <fcntl.h>

因此基本上不用$#include$其餘的東東。

 以後,支持testlib.h$,就不用管以前的選手答案等東西的文件名了

$testlib.h$裏面自帶的文件名有這幾個:

  • inf輸入數據
  • ouf選手輸出
  • ans標準輸出

因此咱們調用的話,直接$inf.***$ $ouf.***$ $ans.**$ 就好

下面說語法:

首先是讀取

讀取方法:

char readChar()

讀入一個char。

char readChar(char c)

和上面同樣,可是隻能讀到一個特定字母c

char readSpace()

讀取一個空格同 readChar(' ').

string readToken()

讀入一個字符串,可是遇到空格、換行、eof爲止、

long long readLong()

讀入一個$long\ long$

long long readLong(long long L, long long R)

同上,可是限定範圍(包括L,R)

int readInt()

讀入一個$int$

int readInt(int L, int R),

同上,可是限定範圍(包括L,R)`

double readReal()

讀入一個實數

double readReal(double L, double R),

同上,可是限定範圍(包括L,R)

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

讀入一個限定範圍精度位數的實數。

string readString(),

string readLine()

讀取一行string,到換行或者eof爲止

void readEoln()

讀入一個換行符

void readEof()

讀入一個$eof$

int eof()

而後怎麼調用這些呢?

咱們從想要的文件裏讀入一個想要的類型的變量

就用$###.***$其中,$###$是文件名(上述三個),$***$是函數名(剛剛說的一堆)

舉個例子,咱們要從選手輸出文件讀取一個$int$

這麼寫就好了

int per_out=ouf.readInt();

這樣就會從選手文件裏讀入一個$int$存到$per_out$裏。

而後就是給出得分和答案

和$fprintf$不是通常的像,

這裏用到的叫$qutif$和$quitp$函數,最少支持1個參數,最多……呵呵

格式:

$quitf(***,string,...)$

首先第一個參數是必須有的

$quitf$可選的值爲:

1.    \ok  //滿分
2.    \wa  //Wown answer ,0分
3.    double變量 //給選手a*100的分 (0<=a<=1)
4.    ceil(100.0*p/a)/100    //給選手p分

中的$1,2$

$quitp$的第一個參數能夠填3$,4$

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

給出AC

quitf(\_wa, "The answer is wrong");

給出WA

quitp(0.5,"Partially Correct");

給出$50\%$的分

quitp(ceil(100.0 * p / a) / 100, "QAQ");

這將會給選手p分。

以後那個字符串就是要顯示的詳情,裏面遵循$printf$語法,就是支持 $\%d$等東西

而後後面跟着幾個要顯示的內容

這裏口糊不太清楚,直接上代碼

 $a+b$的$spj$就能夠這麼寫:

ps:這個直接從簡介裏抄了一份,請見諒

#include "testlib.h"
int main(int argc, char* argv[])
{
    registerTestlibCmd(argc, argv);
    int pans,jans;
    pans=ouf.readInt();
    jans=ans.readInt();
    if (pans == jans)
        quitf(_ok, "Correct. the answer is: %d \n QAQ " , jans);
    else
        quitf(_wa, "WA! expect=%d recieve=%d", jans, pans);
}

 以後,多看幾份$spj$就能本身寫一個不錯的$spj$了

而後呢……

下面奉上一份某沙華大佬的$spj$

額……某沙華大佬表示已經沒題了

不,是沙華不會寫QWQ

 (強烈吐槽一句:LOJ的壓榨翻譯組的人力工做qwq)

相關文章
相關標籤/搜索