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

Posted PushyTao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅谈online judge平台 spj [special judge] 使用 | 修改问题相关的知识,希望对你有一定的参考价值。

以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);///从输入中读取数据组数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;///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[]对应好就没有太大问题

第五种: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);
	以上是关于浅谈online judge平台 spj [special judge] 使用 | 修改问题的主要内容,如果未能解决你的问题,请参考以下文章

如何做一个 Online Judge

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

HDU 1073 Online Judge

HDU 1073 - Online Judge

UVa Online Judge - Home 为啥最近一直登录不了呀,用了360,chrome, 火狐都不行,求指教!!

Online Judge(字符串-格式)