首先你要會寫一個叫$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同樣(沒試過不同行不行)
語言簡稱:c
、cpp
、cpp11
、csharp
、haskell
、java
、lua
、luajit
、nodejs
、pascal
、python2
、python3
、ruby
、vala
、vbnet
、ocam
這裏開始舉個栗子,$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$ 程序運行時,其目錄下會有四個文件 input
、user_out
、answer
、code
,分別對應該測試點的輸入文件、用戶輸出、該測試點的輸出文件、用戶的代碼(對於非提交答案題目)。
你能夠從這四個文件裏讀入東西……
$Special\ Judge$ 程序運行完成後,應將該測試點的得分輸出到標準輸出(stdout
)中(範圍爲 0
到 100
,將自動摺合爲測試點分數),並將提供給用戶的額外信息輸出到標準錯誤輸出(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)