浅谈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使用规则可能不太一样,但是需要改动的地方不是太多

首先:

参数对应

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 <stdio.h>
#include <math.h>
#include <cstring>
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

第二种:多组输入的特判

示例:UVA10886

对应的spj程序应该是:

#include <stdio.h>
#include <math.h>
const double eps = 1e-4;
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;///返回值int T;double a,x;char cas[100],num[100];fscanf(f_in,"%d",&T);///从输入中读取数据组数Twhile(T--){fscanf(f_out,"%s %s %lf",&cas,&num,&a);fscanf(f_user,"%s %s %lf",&cas,&num,&x);if(fabs(a-x)>eps)ret = 1;///Wrong Answer}fclose(f_in);fclose(f_out);fclose(f_user);return ret;
}

第三种:需要判断特殊情况[impossible]


则对应的spj就应该为:

#include <stdio.h>
#include <math.h>
#include <cstring>
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 <fstream>
#include <iostream>
#include <limits>
#include <sstream>
#include <string>
#include <vector>#include <cassert>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 << "Expected: " << s1 << ", got: " << s2 << endl;///被迫对应修改return INCORRECT;}if (s1 != "impossible") {ll x, y;std::stringstream first_token(s2);if (!(first_token >> x) || !(cin >> y)) {debug_output << "Too little output" << endl;///被迫对应修改return INCORRECT;}if (!cin || x <= 0 || y <= 0) {debug_output << "Presentation Error" << endl;///被迫对应修改return INCORRECT;}if (std::numeric_limits<ll>::max() / x < y) {debug_output << "Area too large." << endl;///被迫对应修改return INCORRECT;}ll n;input >> n;vector<ll> cnt(n + 1);for (ll& i: cnt) input >> i;ll current_area = 0;for (int k = n; k >= 0; --k) {ll border_length = 1ll << k;current_area += border_length * border_length * cnt[k];ll a = (x / border_length) * border_length;ll b = (y / border_length) * border_length;if (a * b < current_area) {debug_output << "Incorrect dimensions, cannot fit all squares of size " << border_length << " and larger." << endl;///被迫对应修改return INCORRECT;}}if (current_area != x * y) {debug_output << "Area is " << x * y << ", should be " << current_area << "." << endl;///被迫对应修改return INCORRECT;}}char c;if (cin >> c) {debug_output << "Too much output." << endl;///被迫对应修改return INCORRECT;}return CORRECT;
}

改过之后的spj应该是这样子的:
值得一提的是,这里的输入并不是通过文件指针,而是通过流的方式读写文件
而且GCPC比赛平台的argc[3] 应该是将结果反馈给平台的一个参数

#include <fstream>
#include <iostream>
#include <limits>
#include <sstream>
#include <string>
#include <vector>#include <cassert>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 = 0;
constexpr int INCORRECT = 1;int main(int argc, char* argv[])
{//    assert(argc == 4);ifstream input(argv[1]);ifstream answer(argv[2]);ifstream user(argv[3]);
//    ofstream debug_output(argv[3] + string("/judgemessage.txt"));string s1, s2;answer >> s1;user >> s2;if (s1 != s2 && (s1 == "impossible" || s2 == "impossible")) {return INCORRECT;}if (s1 != "impossible") {ll x, y;std::stringstream first_token(s2);if (!(first_token >> x) || !(user >> y)) {return INCORRECT;}if (!user || x <= 0 || y <= 0) {return INCORRECT;}if (std::numeric_limits<ll>::max() / x < y) {return INCORRECT;}ll n;input >> n;vector<ll> cnt(n + 1);for (ll& i: cnt) input >> i;ll current_area = 0;for (int k = n; k >= 0; --k) {ll border_length = 1ll << k;current_area += border_length * border_length * cnt[k];ll a = (x / border_length) * border_length;ll b = (y / border_length) * border_length;if (a * b < current_area) {return INCORRECT;}}if (current_area != x * y) {return INCORRECT;}}char c;if (user >> c) {return INCORRECT;}return CORRECT;
}
*.h *.cpp的情况

以GCPC 2019 Historical Maths为例:

我们只需要将两个文件合并在一起就好.h文件放在上面,.cpp文件放在下面
如果遇见了某结构体或者是类里面的某个共有或私有函数没有生命的情况,八成加上using namespace std 可以解决

以这个题为例,合并这些文件之后的spj为:

#include <bits/stdc++.h>
/* Utility functions for writing output validators for the Kattis* problem format.** The primary functions and variables available are the following.* In many cases, the only functions needed are "init_io",* "wrong_answer", and "accept".** - init_io(argc, argv):*        initialization** - judge_in, judge_ans, author_out:*        std::istream objects for judge input file, judge answer*        file, and submission output file.** - accept():*        exit and give Accepted!** - accept_with_score(double score):*        exit with Accepted and give a score (for scoring problems)** - judge_message(std::string msg, ...):*        printf-style function for emitting a judge message (a*        message that gets displayed to a privileged user with access*        to secret data etc).** - wrong_answer(std::string msg, ...):*        printf-style function for exitting and giving Wrong Answer,*        and emitting a judge message (which would typically explain*        the cause of the Wrong Answer)** - judge_error(std::string msg, ...):*        printf-style function for exitting and giving Judge Error,*        and emitting a judge message (which would typically explain*        the cause of the Judge Error)** - author_message(std::string msg, ...):*        printf-style function for emitting an author message (a*        message that gets displayed to the author of the*        submission).  (Use with caution, and be careful not to let*        it leak information!)**/#include <sys/stat.h>
#include <cassert>
#include <cstdarg>
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <sstream>typedef void (*feedback_function)(const std::string &, ...);const int EXITCODE_AC = 42;///注释 / 修改
const int EXITCODE_WA = 43;///注释 / 修改
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 < author_out\n"std::ifstream judge_in, judge_ans;
std::istream author_out(std::cin.rdbuf());///修改char *feedbackdir = NULL;void vreport_feedback(const std::string &category,const std::string &msg,va_list pvar) {std::ostringstream fname;if (feedbackdir)fname << feedbackdir << '/';fname << category;FILE *f = fopen(fname.str().c_str(), "a");assert(f);vfprintf(f, msg.c_str(), pvar);fclose(f);
}void report_feedback(const std::string &category, const std::string &msg, ...) {va_list pvar;va_start(pvar, msg);vreport_feedback(category, msg, pvar);
}void author_message(const std::string &msg, ...) {va_list pvar;va_start(pvar, msg);vreport_feedback(FILENAME_AUTHOR_MESSAGE, msg, pvar);
}void judge_message(const std::string &msg, ...) {va_list pvar;va_start(pvar, msg);vreport_feedback(FILENAME_JUDGE_MESSAGE, msg, pvar);
}void wrong_answer(const std::string &msg, ...) {va_list pvar;///注释 / 修改va_start(pvar, msg);vreport_feedback(FILENAME_JUDGE_MESSAGE, msg, pvar);exit(EXITCODE_WA);
}void judge_error(const std::string &msg, ...) {va_list pvar;///注释 / 修改va_start(pvar, msg);vreport_feedback(FILENAME_JUDGE_ERROR, msg, pvar);assert(0);
}void accept() {exit(EXITCODE_AC);
}void accept_with_score(double scorevalue) {report_feedback(FILENAME_SCORE, "%.9le", scorevalue);exit(EXITCODE_AC);
}bool is_directory(const char *path) {struct stat entry;return stat(path, &entry) == 0 && S_ISDIR(entry.st_mode);
}void init_io(int argc, char **argv) {if(argc < 4) {///注释 / 修改fprintf(stderr, USAGE, argv[0]);///注释 / 修改judge_error("Usage: %s judgein judgeans feedbackdir [opts] < userout", argv[0]);///注释 / 修改}///注释 / 修改
///注释 / 修改// Set up feedbackdir first, as that allows us to produce feedback///注释 / 修改// files for errors in the other parameters.///注释 / 修改if (!is_directory(argv[3])) {///注释 / 修改judge_error("%s: %s is not a directory\n", argv[0], argv[3]);///注释 / 修改}///注释 / 修改feedbackdir = argv[3];///注释 / 修改judge_in.open(argv[1], std::ios_base::in);if (judge_in.fail()) {judge_error("%s: failed to open %s\n", argv[0], argv[1]);}judge_ans.open(argv[2], std::ios_base::in);if (judge_ans.fail()) {judge_error("%s: failed to open %s\n", argv[0], argv[2]);}author_out.rdbuf(std::cin.rdbuf());///注释 / 修改
}using namespace std;
using ll = long long;
using vl = vector<ll>;
#define sz(c) ll((c).size())
#define FOR(i,a,b) for(ll i = (a); i < (b); i++)
#define FORD(i,a,b) for(ll i = ll(b) - 1; i >= (a); i--)const ll MAX_BASE = (2LL << 60) + 1;ll check_and_parse_author(const string& to_parse) {//first pass; check for invalid characterif(sz(to_parse) == 0) wrong_answer("Submission provided empty string to parse.\n");if(to_parse[0] == '+' && sz(to_parse) == 1) wrong_answer("Answer is not a number.\n");if(to_parse[0] != '+' && (to_parse[0] < '0' || '9' < to_parse[0])) wrong_answer("Answer contains invalid character.\n");FOR(i,1,sz(to_parse)) if(to_parse[i] < '0' || '9' < to_parse[i]) wrong_answer("Answer contains invalid character.\n");//second pass; calculate answer basell base = 0;if(to_parse[0] != '+') base = to_parse[0] - '0';FOR(i,1,sz(to_parse)) {// avoid overflowif(MAX_BASE / 10 < base) return MAX_BASE;base = base*10 + (to_parse[i] - '0');}return base;
}
void multiply(vl &a, vl &b, vl &res, ll base) {res.assign(sz(res), 0);FOR(i,0,sz(a)) {FOR(j,0,sz(b)) {res[i + j] += a[i] * b[j];res[i + j + 1] += res[i + j] / base;res[i + j] %= base;}}FOR(i,0,sz(res) - 1) {res[i + 1] += res[i] / base;res[i] %= base;}
}ll compare(vl &a, vl &b) {if(sz(a) < sz(b)) {FOR(i,sz(a),sz(b)) if(b[i] != 0) return -1;}if(sz(a) > sz(b)) {FOR(i,sz(b), sz(a)) if(a[i] != 0) return 1;}FORD(i,0,min(sz(a),sz(b))) {if(a[i] - b[i] != 0) return a[i] - b[i];}return 0;
}int main(int argc, char **argv) {init_io(argc,argv);///注释 / 修改这个函数string a_ans, j_ans;char foo;judge_ans >> j_ans;if(!( author_out >> 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 acceptif(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 < 2) wrong_answer("Invalid base.\n");bool differs = false;if(j_ans == "impossible") differs = true;ll tmp, maxdigit = 1;judge_in >> 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 <bits/stdc++.h>
#include <sys/stat.h>
#include <cassert>
#include <cstdarg>
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <sstream>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 < author_out\n"std::ifstream judge_in, judge_ans;
std::ifstream author_out;char *feedbackdir = NULL;void vreport_feedback(const std::string &category,const std::string &msg,va_list pvar) {std::ostringstream fname;if (feedbackdir)fname << feedbackdir << '/';fname << category;FILE *f = fopen(fname.str().c_str(), "a");assert(f);vfprintf(f, msg.c_str(), pvar);fclose(f);
}void report_feedback(const std::string &category, const std::string &msg, ...) {va_list pvar;va_start(pvar, msg);vreport_feedback(category, msg, pvar);
}void author_message(const std::string &msg, ...) {va_list pvar;va_start(pvar, msg);vreport_feedback(FILENAME_AUTHOR_MESSAGE, msg, pvar);
}void judge_message(const std::string &msg, ...) {va_list pvar;va_start(pvar, msg);vreport_feedback(FILENAME_JUDGE_MESSAGE, msg, pvar);
}void wrong_answer(const std::string &msg, ...) {//  va_list pvar;
//  va_start(pvar, msg);
//  vreport_feedback(FILENAME_JUDGE_MESSAGE, msg, pvar);exit(EXITCODE_WA);
}void judge_error(const std::string &msg, ...) {//  va_list pvar;
//  va_start(pvar, msg);
//  vreport_feedback(FILENAME_JUDGE_ERROR, msg, pvar);assert(0);
}void accept() {exit(EXITCODE_AC);
}void accept_with_score(double scorevalue) {//  report_feedback(FILENAME_SCORE, "%.9le", scorevalue);exit(EXITCODE_AC);
}bool is_directory(const char *path) {struct stat entry;return stat(path, &entry) == 0 && S_ISDIR(entry.st_mode);
}void init_io(int argc, char **argv) {//  if(argc < 4) {//      fprintf(stderr, USAGE, argv[0]);
//      judge_error("Usage: %s judgein judgeans feedbackdir [opts] < userout", argv[0]);
//  }
//
//  // Set up feedbackdir first, as that allows us to produce feedback
//  // files for errors in the other parameters.
//  if (!is_directory(argv[3])) {//      judge_error("%s: %s is not a directory\n", argv[0], argv[3]);
//  }
//  feedbackdir = argv[3];judge_in.open(argv[1], std::ios_base::in);if (judge_in.fail()) {judge_error("%s: failed to open %s\n", argv[0], argv[1]);}judge_ans.open(argv[2], std::ios_base::in);if (judge_ans.fail()) {judge_error("%s: failed to open %s\n", argv[0], argv[2]);}author_out.open(argv[3], std::ios_base::in);if (author_out.fail()) {judge_error("%s: failed to open %s\n", argv[0], argv[3]);}
}using namespace std;
using ll = long long;
using vl = vector<ll>;
#define sz(c) ll((c).size())
#define FOR(i,a,b) for(ll i = (a); i < (b); i++)
#define FORD(i,a,b) for(ll i = ll(b) - 1; i >= (a); i--)const ll MAX_BASE = (2LL << 60) + 1;ll check_and_parse_author(const string& to_parse) {//first pass; check for invalid characterif(sz(to_parse) == 0) wrong_answer("Submission provided empty string to parse.\n");if(to_parse[0] == '+' && sz(to_parse) == 1) wrong_answer("Answer is not a number.\n");if(to_parse[0] != '+' && (to_parse[0] < '0' || '9' < to_parse[0])) wrong_answer("Answer contains invalid character.\n");FOR(i,1,sz(to_parse)) if(to_parse[i] < '0' || '9' < to_parse[i]) wrong_answer("Answer contains invalid character.\n");//second pass; calculate answer basell base = 0;if(to_parse[0] != '+') base = to_parse[0] - '0';FOR(i,1,sz(to_parse)) {// avoid overflowif(MAX_BASE / 10 < base) return MAX_BASE;base = base*10 + (to_parse[i] - '0');}return base;
}
void multiply(vl &a, vl &b, vl &res, ll base) {res.assign(sz(res), 0);FOR(i,0,sz(a)) {FOR(j,0,sz(b)) {res[i + j] += a[i] * b[j];res[i + j + 1] += res[i + j] / base;res[i + j] %= base;}}FOR(i,0,sz(res) - 1) {res[i + 1] += res[i] / base;res[i] %= base;}
}ll compare(vl &a, vl &b) {if(sz(a) < sz(b)) {FOR(i,sz(a),sz(b)) if(b[i] != 0) return -1;}if(sz(a) > sz(b)) {FOR(i,sz(b), sz(a)) if(a[i] != 0) return 1;}FORD(i,0,min(sz(a),sz(b))) {if(a[i] - b[i] != 0) return a[i] - b[i];}return 0;
}int main(int argc, char **argv) {init_io(argc,argv);string a_ans, j_ans;char foo;judge_ans >> j_ans;if(!( author_out >> 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 acceptif(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 < 2) wrong_answer("Invalid base.\n");bool differs = false;if(j_ans == "impossible") differs = true;ll tmp, maxdigit = 1;judge_in >> 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 < 4 || argc > 6)// {//     quit(_fail, std::string("Program must be run with the following arguments: ") +//         std::string("<input-file> <output-file> <answer-file> [<report-file> [<-appes>]]") +//         "\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: ") +//                     "<input-file> <output-file> <answer-file> [<report-file> [<-appes>]]");//     }//     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  < 3 || argc > 5)quit(_fail, std::string("Program must be run with the following arguments: ") +"<input-file> <output-file> <answer-file> [<report-file> [<-appes>]]");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 <algorithm>
#include <stdexcept>
#include <fstream>
#include <iostream>
#include <limits>
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 < argc; ++i) {if(argv[i] == case_sensitive_flag) case_sensitive = true;if(argv[i] == space_change_sensitive_flag) ws = true;}if(ws) in >> 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 << "read space!\n";}void newline() {if(ws) {char c;in >> c;if(c != '\n') expected("newline", string("\"") + c + "\"");}// cerr << "read newline!\n";}// Just read a string.string read_string() { return read_string_impl(); }// Read a string and make sure it equals `expected`.string read_string(string expected) { return read_string_impl(expected); }// Read an arbitrary string of a given length.string read_string(size_t min, size_t max) {string s = read_string();if(s.size() < min || s.size() > 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 <= v && v <= high) return v;expected("integer between " + to_string(low) + " and " + to_string(high), to_string(v));}int read_int() {return read_long_long(std::numeric_limits<int>::min(), std::numeric_limits<int>::max());}int read_int(int low, int high) {int v = read_long_long(std::numeric_limits<int>::min(), std::numeric_limits<int>::max());if(low <= v && v <= high) return v;expected("integer between " + to_string(low) + " and " + to_string(high), to_string(v));}// Read a long double.long double read_long_double() {string s = read_string_impl("", "integer");long double v;try {size_t chars_processed;v = stold(s, &chars_processed);if(chars_processed != s.size())WA("Parsing ", s, " as long double failed! Did not process all characters.");} catch(const out_of_range &e) {WA("Number " + s + " does not fit in a long double!");} catch(const invalid_argument &e) { WA("Parsing " + s + " as long double failed!"); }return v;}// Check the next character.bool peek(char c) {if(!ws) in >> ::ws;return in.peek() == char_traits<char>::to_int_type(c);}// Return WRONG ANSWER verdict.[[noreturn]] void expected(string exp = "", string s = "") {if(s.size())cout << "Expected " << exp << ", found " << s << endl;else if(exp.size())cout << exp << endl;exit(ret_WA);}template <typename T>[[noreturn]] void WA(T t) {cout << t << endl;exit(ret_WA);}template <typename T, typename... Ts>[[noreturn]] void WA(T t, Ts... ts) {cout << t;WA(ts...);}template <typename... Ts>void assert(bool b, Ts... ts) {if(!b) WA(ts...);}private:// Read an arbitrary string.// expected: if not "", string must equal this.// wanted: on failure, print "expected <wanted>, got ..."string read_string_impl(string expected_string = "", string wanted = "string") {if(ws) {char next = in.peek();if(isspace(next)) expected(wanted, "whitespace");}string s;if(in >> s) {if(!case_sensitive) {s               = lowercase(s);expected_string = lowercase(expected_string);}if(!expected_string.empty() && s != expected_string)WA("Expected string \"expected\", but found ", s);return s;}expected(wanted, "nothing");}// Return ACCEPTED verdict.[[noreturn]] void AC() { exit(ret_AC); }void eof() {if(in.eof()) return;// Sometimes EOF hasn't been triggered yet.if(!ws) in >> ::ws;char c = in.get();if(c == char_traits<char>::eof()) return;expected("EOF", string("\"") + char(c) + "\"");}// Convert a string to lowercase is matching is not case sensitive.string &lowercase(string &s) {if(!case_sensitive) return s;transform(s.begin(), s.end(), s.begin(), ::tolower);return s;}istream &in;
};

我们尤其需要注意公有成员方法:

Validator(int argc, char **argv, istream &in = std::cin) : in(in) {for(int i = 0; i < argc; ++i) {if(argv[i] == case_sensitive_flag) case_sensitive = true;if(argv[i] == space_change_sensitive_flag) ws = true;}if(ws) in >> noskipws;}

main中的前几行代码:

// Set up the input and answer streams.std::ifstream in(argv[1]);std::ifstream ans(argv[2]);

因为本平台都是使用文件的形式进行判断所提交的代码,这里可以用流的形式进行操作,将42、43改成对应的0、1然后将main中添加如下代码:

// Set up the input and answer streams.std::ifstream in(argv[1]);std::ifstream ans(argv[2]);std::ifstream user(argv[3]);Validator out(argc, argv, user);// 以下代码为输入

然后将公有成员构造方法改成:

Validator(int argc, char **argv, std::ifstream &in) : in(in) {for(int i = 0; i < argc; ++i) {if(argv[i] == case_sensitive_flag) case_sensitive = true;if(argv[i] == space_change_sensitive_flag) ws = true;}if(ws) in >> noskipws;
}

即可完美解决

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

  1. 浅谈企业级物联网平台

    随著越来越多的公司开始涉足物联网相关领域,这也意味着会有很多原来是做互联网项目开发的同学也不得不开始学习物联网的开发,同样的对于项目经理来说,如何确定一个满足业务需求的物联网技术架构则至关重要,笔者根 ...

  2. 【教程】如何正确的写一个Lemon/Cena的SPJ(special judge)

    Special Judge:当正确的输出结果不唯一的时候需要的自定义校验器 首先有个框架 #include<fstream> ifstream fin,fout,fstd ofstream ...

  3. Lemon LemonLime 中 SPJ Special Judge 使用 实践 入门 a

    精度需要SPJ 入门: 题目,以整数形式给定圆的半径,输出该圆的周长,该圆的面积. 比赛目录如下: 标准输入输出数据如下: circle1.in 1 circle1.ans 6.283185 3.14 ...

  4. 浅谈Horizon DaaS平台 - 崛起的桌面云平台

    随着技术的发展以及工作模式的改变,企业面临越来越多的在IT方面的挑战.比如越来越多的员工希望使用自己的设备来进行日常工作,无论是在办公室中还是在家中都可以方便的进行工作.这些新的需求带来了各个方面的挑 ...

  5. 浅谈管理数据平台的一些想法

    前言: 对于任何使用大数据技术的公司来说,大数据平台特别是Hive来说,维护其高效快速的运行,对整个公司的运作来说至关重要.比如说:某个调度任务失败了造成业务部门的某些报表无法正常产出:hive平台最 ...

  6. 浅谈微信公众平台运用的场景

    微信,是一个生活方式 超过三亿人使用的手机应用 支持发送语音短信.视频.图片和文字 可以群聊,仅耗少量流量,适合大部分智能手机 上面是微信官方网站的微信说明.这两年,随着智能手机的普及,微信得到迅猛的 ...

  7. 浅谈快速开发平台:突破系统开发边界,赋能企业数字化!

    快速开发平台,简单地说就是指那些不用编码或通过少量代码,就可以快速开发应用程序的平台.既可以降低开发人力成本,又可以缩短开发时间,从而实现企业降本增效的价值. 今天为大家介绍4款成熟业务的快速开发平台 ...

  8. 浅谈大数据平台架构设计

    全文共3735个字,建议8分钟阅读 近年来,随着IT技术与大数据.机器学习.算法方向的不断发展,越来越多的企业都意识到了数据存在的价值,将数据作为自身宝贵的资产进行管理,利用大数据和机器学习能力去挖掘 ...

  9. 浅谈数据标注平台运营模式

    现如今,AI行业发展火热各大巨头都投入巨资在此领域布局,智能驾驶.人脸识别以及近段时间正火的AI养猪都是AI技术应用在实际生活上的体现,毫不夸张的说AI技术正在逐渐改变我们的生活而我们的生活也将因此变 ...

  10. 浅谈大数据平台的数据治理

    近几年大数据风生水起,随着大数据业务的不断开展,各大互联网公司包括一些传统企业都越老越重视数据价值的挖掘. 一.数据治理模型 在公司的日常运行中,各种数据分析和数据挖掘技术,为公司发展决策和业务开展提 ...

最新文章

  1. TPF111视频信号放大器研究
  2. 成功解决pillow模块内生成的非UTF-8格式(比如label中有汉字时)
  3. oracle arp绑定mac地址,使用ARP命令来绑定IP和MAC地址
  4. verilog的“==”与“===”
  5. neo4j 删 delete
  6. 深度学习pytorch基础入门教程(1小时)-神经网络
  7. 1.业务架构·应用架构·数据架构实战 --- 架构实践全景图
  8. WPS快捷键之 EXCEL高级
  9. Java计算文件MD5值
  10. 在浏览器中输入URL(如www.baidu.com)到显示页面经历哪些过程,涉及到哪些协议?
  11. 大数据算法工程师知识点大全
  12. 洛谷P3373 【模板】线段树 2
  13. Android启动活动用什么方法,Android - 使用intent uri从命令行启动活动
  14. Java Dad08
  15. 个人管理 - 时间管理GTD流程图
  16. react手机端项目注意点,ant design-mobile框架
  17. SQL SERVER Mail
  18. Verilog 流水线设计
  19. 谷歌生物医学专用翻译_一个可以快速翻译浏览英文文献的工具,拿走不谢!
  20. 计算机教师使用计划书,信息技术教师个人发展计划

热门文章

  1. 消融实验(Ablation study)的最佳解释
  2. 超声波传感器(CHx01) 学习笔记 Ⅲ - I2C读写操作
  3. 修真院教学模式四大体系之职业素养
  4. 华为HCNA实验学习
  5. win7电脑变身WiFi热点
  6. druid监控页面 关闭_druid-springboot-starter默认启用监控页面,公网项目可能外泄DB信息...
  7. JVM-由常量池 运行时常量池 String intern方法想到的(三)之String内存模型
  8. LTE Attach
  9. PCB板子焊接注意事项及小技巧
  10. Android亮度调节