浅谈oj平台 spj 使用 | 修改问题
-
- 首先:
-
- 参数对应
- 返回值
- 代码提交
- 几种spj
-
- 第一种:简单的一类特判
- 第二种:多组输入的特判
- 第三种:需要判断特殊情况[impossible]
- 第四种:带有[testlib.h]的spj
- 第五种:GCPC [German Collegiate Programming Contest] 类spj
-
- 单个文件的情况
- *.h *.cpp的情况
- 第六种:交互题的spj
- 第七种[带有testlib.h]的另一种解决方式
- 第八种 使用validation.h的BAPC2018(较难)
以LDUOJ为例 goto -> github
LDUOJ 平台开发者spj开发博客
不同的平台的spj使用规则可能不太一样,但是需要改动的地方不是太多
首先:
附LDUOJ之父赵京龙学长的特判使用文档地址:winterant.github.io/OnlineJudge…
参数对应
args[1] 对应数据的输入
args[2] 对应数据的答案也就是一种允许的情况 或 impossible的情况(下面会讲到)
args[3] 对应用户结果的输出,也就是需要重点关注的地方
返回值
0代表没有问题即 AC
1代表出现问题即 WA
对于有的系统来说
42代表AC,43代表WA,不同的系统可能是不一样的返回值
代码提交
和牛客平台等类似,提交的Java代码主类的类名必须是Main
否则会编译错误
就比如应该是:
public class Main{
public static void main(){
/**
your code
**/
}
}
几种spj
第一种:简单的一类特判
比如下面这个比较简单的spj程序:
#include
#include
#include
const double eps = 1e-6;
int main(int argc,char *args[])
{
FILE * f_in=fopen(args[1],"r");
FILE * f_out=fopen(args[2],"r");
FILE * f_user=fopen(args[3],"r");
fclose(f_in);
fclose(f_out);
fclose(f_user);
return ret;
}
用文件指针的情况,我们可以直接用fscanf 进行输入输出
格式如下:
fscanf(pnt,"%d",&x);
其中pnt为想要从哪里读取的文件指针。比如要获取用户的输出,就要将pnt替换为f_user
以上方式写出来的spj可能不太严谨,建议换用新的模板,这个模板是存在于文档中:
#include
using namespace std;
#define AC 0
#define WA 1
void jscanf(FILE *&fin, const char *format, ...) {
va_list args;
va_start(args, format); /* 初始化变长参数列表 */
int ret = vfscanf(fin, format, args);
va_end(args); /* 结束使用变长参数列表 */
if (ret == EOF) {
printf("When reading data, the program crashes because EOF is encountered in advance.n");
exit(WA);
}
}
bool is_whitespace(const char c) {
return c == ' ' || c == 't' || c == 'n' || c == 'r';
}
//检查用户是否存在多余输出
int read_until_eof(FILE *&fp) {
char ch = fgetc(fp);
while (ch != EOF && is_whitespace(ch)) {
ch = fgetc(fp);
}
if (ch != EOF) {
printf("There is redundant content in user outputn");
return WA;
}
return AC;
}
int judge(FILE *&std_in, FILE *&std_out, FILE *&user_out);
int main(int argc, char *args[]) {
if (argc eps)
ret = 1;///Wrong Answer
}
fclose(f_in);
fclose(f_out);
fclose(f_user);
return ret;
}
第三种:需要判断特殊情况[impossible]
则对应的spj就应该为:
#include
#include
#include
const double eps = 1e-6;
int main(int argc,char *args[])
{
FILE * f_in=fopen(args[1],"r");
FILE * f_out=fopen(args[2],"r");
FILE * f_user=fopen(args[3],"r");
int ret = 0;
double a,x;
char std[100],usr[100];
while(fscanf(f_out,"%s",std) == 1 && fscanf(f_user,"%s",usr) == 1){
if(strcmp(std,"IMPOSSIBLE") && !strcmp(usr,"IMPOSSIBLE"))
{
ret = 1;
return ret;
}
if(strcmp(usr,"IMPOSSIBLE") && !strcmp(std,"IMPOSSIBLE"))
{
ret = 1;
return ret;
}
double sstd = atof(std);
double uusr = atof(usr);
if(fabs(sstd - uusr) > eps) {
ret = 1;
return ret;
}
}
fclose(f_in);
fclose(f_out);
fclose(f_user);
return ret;
}
第四种:带有[testlib.h]的spj
一般情况下,这种题目的spj都是比较规范的,而且testlib.h是在Github上进行开源的
该头文件的发明者应该是Codeforces的管理员 MikeMirzayanov
只需要将上述中的args[]对应好就没有太大问题
2021-09-16更新
在处理的过程当中,笔者发现大部分的testlib.h类的spj与lduoj是匹配的
所以说在处理的时候如果发现spj编译报错,那么就说明用到的checker.cpp和testlib.h的版本不对应,应该参考附带的testlib.h,然后拼接在一起
如果发现更改spj之后还不通过std代码,首先可以试试所有的语言的标程,如果还是不通过,可以确定是spj的问题,这里就需要对spj统一换成比较朴实的spj,具体什么格式可以参考文章首部的oj开发者提供的spj使用规范
需要重新实现一下使用到的testlib.h里面的函数方法即可
第五种:GCPC [German Collegiate Programming Contest] 类spj
单个文件的情况
这种情况比较简单
以GCPC2019 Keeping the Dogs Out为例:
打开可以看到:
#include
#include
#include
#include
#include
#include
#include
typedef long long ll;
using std::cin;
using std::endl;
using std::ifstream;
using std::ofstream;
using std::string;
using std::vector;
constexpr int CORRECT = 42;///修改
constexpr int INCORRECT = 43;///修改
int main(int argc, char* argv[])
{
assert(argc == 4);
ifstream input(argv[1]);
ifstream answer(argv[2]);
ofstream debug_output(argv[3] + string("/judgemessage.txt"));///修改
string s1, s2;
answer >> s1;
cin >> s2;
if (s1 != s2 && (s1 == "impossible" || s2 == "impossible")) {
debug_output > foo) wrong_answer("More output than expected.n");
transform(a_ans.begin(), a_ans.end(), a_ans.begin(), ::tolower);
//quick accept
if(a_ans == j_ans) accept();
if(a_ans == "impossible") wrong_answer("Submission claims impossible, judge has answer.n");
ll base = check_and_parse_author(a_ans);
if(base > tmp;
vl a(tmp);
FORD(i, 0, tmp) {judge_in >> a[i]; maxdigit = max(maxdigit, a[i]);}
judge_in >> tmp;
vl b(tmp);
FORD(i, 0, tmp){ judge_in >> b[i]; maxdigit = max(maxdigit, b[i]);}
judge_in >> tmp;
vl prod(tmp);
FORD(i, 0, tmp){ judge_in >> prod[i]; maxdigit = max(maxdigit, prod[i]);}
vl res(sz(a) + sz(b) + 1);
if(base < maxdigit + 1) wrong_answer("Base not greater than all occuring digits.n");
multiply(a,b,res,base);
ll cmp = compare(prod, res);
if(cmp == 0) {
if(differs)
judge_error("Judge answer is 'impossible' but submission gave valid answer.n");
accept();
}
wrong_answer("Invalid base.n");
}
由于平台不同的原因,在函数含有某行对文件写操作的代码会出现问题,所以要注释掉,还要将返回的状态码改成0 1,而不是使用42 43
注意有些头文件在Windows平台下并不能使用,会报出编译错误,但是在Linux平台下却是可以的,提交spj之后会编译成功
需要修改的地方已在上面的代码中加入了批注,然后,修改之后应该是:
#include
#include
#include
#include
#include
#include
#include
#include
typedef void (*feedback_function)(const std::string &, ...);
const int EXITCODE_AC = 0;
const int EXITCODE_WA = 1;
const std::string FILENAME_AUTHOR_MESSAGE = "teammessage.txt";
const std::string FILENAME_JUDGE_MESSAGE = "judgemessage.txt";
const std::string FILENAME_JUDGE_ERROR = "judgeerror.txt";
const std::string FILENAME_SCORE = "score.txt";
#define USAGE "%s: judge_in judge_ans feedback_dir > a_ans)) wrong_answer("Less output than expected.n");
if(author_out >> foo) wrong_answer("More output than expected.n");
transform(a_ans.begin(), a_ans.end(), a_ans.begin(), ::tolower);
//quick accept
if(a_ans == j_ans) accept();
if(a_ans == "impossible") wrong_answer("Submission claims impossible, judge has answer.n");
ll base = check_and_parse_author(a_ans);
if(base > tmp;
vl a(tmp);
FORD(i, 0, tmp) {
judge_in >> a[i];
maxdigit = max(maxdigit, a[i]);
}
judge_in >> tmp;
vl b(tmp);
FORD(i, 0, tmp) {
judge_in >> b[i];
maxdigit = max(maxdigit, b[i]);
}
judge_in >> tmp;
vl prod(tmp);
FORD(i, 0, tmp) {
judge_in >> prod[i];
maxdigit = max(maxdigit, prod[i]);
}
vl res(sz(a) + sz(b) + 1);
if(base < maxdigit + 1) wrong_answer("Base not greater than all occuring digits.n");
multiply(a,b,res,base);
ll cmp = compare(prod, res);
if(cmp == 0) {
if(differs)
judge_error("Judge answer is 'impossible' but submission gave valid answer.n");
accept();
}
wrong_answer("Invalid base.n");
}
第六种:交互题的spj
本平台暂不支持交互题,所以题库里的交互题目前没有进行处理通过
可以参考洛谷的交互题spj对应写法
第七种[带有testlib.h]的另一种解决方式
…遇见后后续更新
2021-10-21更新
将以下文件入口对应
inf->标准输入 argv[1]
ouf->用户输出 argv[3]
ans->答案结果 argv[2]
void registerTestlibCmd(int argc, char* argv[]) {
__testlib_ensuresPreconditions();
testlibMode = _checker;
__testlib_set_binary(stdin);
if (argc > 1 && !strcmp("--help", argv[1]))
__testlib_help();
// if (argc 6)
// {
// quit(_fail, std::string("Program must be run with the following arguments: ") +
// std::string(" [ []]") +
// "nUse "--help" to get help information");
// }
appesMode = false;
// if (argc == 3) {///改
// resultName = "";
// appesMode = false;
// }
//
// if (argc == 4) {
// resultName = make_new_file_in_a_dir(argv[3]);
// appesMode = false;
// }///改
// if (argc == 6)
// {
// if (strcmp("-APPES", argv[5]) && strcmp("-appes", argv[5]))
// {
// quit(_fail, std::string("Program must be run with the following arguments: ") +
// " [ []]");
// }
// else
// {
// resultName = argv[4];
// appesMode = true;
// }
// }
inf.init(argv[1], _input);
ouf.init(argv[3], _output);
ans.init(argv[2], _answer);/// 改
}
void registerTestlib(int argc, ...) {
if (argc 5)
quit(_fail, std::string("Program must be run with the following arguments: ") +
" [ []]");
char** argv = new char*[argc + 1];
va_list ap;
va_start(ap, argc);
argv[0] = NULL;
for (int i = 0; i < argc; i++) {
argv[i + 1] = va_arg(ap, char*);
}
va_end(ap);
registerTestlibCmd(argc + 1, argv);
delete[] argv;
}
第八种 使用validation.h的BAPC2018(较难)
首先站是原始的BAPC后台validation.h文件:
// A header library to safely parse team input.
// It does not support floating points or big integers.
// The easiest way to use this is to symlink it from a validator directory,
// so that it will be picked up when creating a contest zip.
// The default checking behaviour is lenient for both white space and case.
// When validating .in and .ans files, the case_sensitve and space_change_sensitive flags should be
// passed. When validating team output, the flags in problem.yaml should be used.
#include
#include
#include
#include
#include
using namespace std;
const string case_sensitive_flag = "case_sensitive";
const string space_change_sensitive_flag = "space_change_sensitive";
class Validator {
const int ret_AC = 42, ret_WA = 43;
bool case_sensitive;
bool ws;
public:
Validator(int argc, char **argv, istream &in = std::cin) : in(in) {
for(int i = 0; i > noskipws;
}
// No copying, no moving.
Validator(const Validator &) = delete;
Validator(Validator &&) = delete;
// At the end of the scope, check whether the EOF has been reached.
// If so, return AC. Otherwise, return WA.
~Validator() {
eof();
AC();
}
void space() {
if(ws) {
char c;
in >> c;
if(c != ' ') expected("space", string(""") + c + """);
}
// cerr > c;
if(c != 'n') expected("newline", string(""") + c + """);
}
// cerr max)
expected("String of length between " + to_string(min) + " and " + to_string(max), s);
return s;
}
// Read the string t.
void test_string(string t) {
string s = read_string();
if(case_sensitive) {
if(s != t) expected(t, s);
} else {
if(lowercase(s) != lowercase(t)) expected(t, s);
}
}
// Read a long long.
long long read_long_long() {
string s = read_string_impl("", "integer");
long long v;
try {
size_t chars_processed = 0;
v = stoll(s, &chars_processed);
if(chars_processed != s.size())
WA("Parsing " + s + " as long long failed! Did not process all characters");
} catch(const out_of_range &e) {
WA("Number " + s + " does not fit in a long long!");
} catch(const invalid_argument &e) { WA("Parsing " + s + " as long long failed!"); }
return v;
}
// Read a long long within a given range.
long long read_long_long(long long low, long long high) {
auto v = read_long_long();
if(low