首先你要会写一个叫$data.yml$的东西,
这里面记录了这道题的$subtask$计分策略
也告诉了评测姬这道题是提交答案还是$spj$还是交互题
那么,$YAML$语言是啥啊?
别问我,我也不会 本着会用能用就行的原则
给大家讲讲$LOJ$的$special\ jidge$怎么用
$yaml$里基本上的全部语法:
$Structure$通过空格来展示。$Sequence$里的项用$-$来代表,Map里的键值对用$:$分隔.
在$LOJ$我们需要写一份类似这样的$data.yml$文件:
(摘自 LOJ帮助 ,稍有改动)
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$
正在更新……