以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(){
}
}
几种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);
while(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;
}
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[]对应好就没有太大问题
第五种: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[])
{
ifstream input(argv[1]);
ifstream answer(argv[2]);
ifstream user(argv[3]);
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>
#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]);
}
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) {
if(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");
ll base = 0;
if(to_parse[0] != '+') base = to_parse[0] - '0';
FOR(i,1,sz(to_parse)) {
if(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);
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 < 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, ...) {
exit(EXITCODE_WA);
}
void judge_error(const std::string &msg, ...) {
assert(0);
}
void accept() {
exit(EXITCODE_AC);
}
void accept_with_score(double 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) {
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) {
if(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");
ll base = 0;
if(to_parse[0] != '+') base = to_parse[0] - '0';
FOR(i,1,sz(to_parse)) {
if(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);
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 < 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对应写法
第七种…遇见后后续更新
|