浅谈online judge平台 spj [special judge] 使用 | 修改问题

2023年 9月 17日 56.4k 0

浅谈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

相关文章

服务器端口转发,带你了解服务器端口转发
服务器开放端口,服务器开放端口的步骤
产品推荐:7月受欢迎AI容器镜像来了,有Qwen系列大模型镜像
如何使用 WinGet 下载 Microsoft Store 应用
百度搜索:蓝易云 – 熟悉ubuntu apt-get命令详解
百度搜索:蓝易云 – 域名解析成功但ping不通解决方案

发布评论