洛谷 的博客

交互题功能说明

交互题既用户提交的程序,通过出题人提供的交互库,与判题程序(SPJ)进行交互并获得输入、解答问题。

洛谷上的交互题评测,由以下三个部分组成:判题程序(SPJ)、交互库、用户程序。其中出题人需要提供 SPJ 以及交互库。交互题需要打上 交互题 标签。如果使用 SPJ 功能,还需要 Special Judge 标签。

在数据压缩包中,除了测试点输入输出文件,还需包含 checker.cpp SPJ 程序文件、以及 interactive_lib.cpp 交互库文件。与 SPJ 相同,均使用 C++14 标准进行编译,不支持其他语言。

  • 交互题的 SPJ 写法

交互题可以不使用 SPJ 功能。不使用的情况下,交互库将直接读取测试点输入,并由默认比较器进行输出比较。和普通题目相比,除了链接交互库到用户程序外没有其他区别。

交互题的 SPJ 与普通的 Special Judge 类似,使用 testlib 。具体请参考 Testlib 文档 以及 Special Judge 使用说明

需要注意的是,该 SPJ 同时承担判题和与交互库交换数据两个功能。交换数据通过标准输入输出进行,既 SPJ 的 stdout 可在交互库的 stdin 中读出,交互库的 stdout 可在 SPJ 的 stdin 中读出。

这里给出一个简单的例子:

#include "testlib.h"
#include <iostream>

using namespace std;

int main(int argc, char* argv[])
{
    setName("Interactor A+B");
    registerInteraction(argc, argv);

    // 从测试数据中读取输入
    int a = inf.readInt();
    int b = inf.readInt();

    // 发送给交互库
    cout << a << " " << b << endl;

    // 读取交互库的输出。直接使用 cin 或者 scanf 也可以。
    int pa = ouf.readInt();
    int ja = a + b;

    if (ja != pa) quitf(_wa, "expected %d, found %d", ja, pa);
    quitf(_ok, "answer is %d", ja);
}
  • 交互库的写法

交互库 interactive_lib.cpp 是被链接到用户程序的一个模块,包含或者不包含 main 函数均可。理论上如果你希望用户直接与 SPJ 通过标准输入输出交互,直接什么都不提供也可以,但这种情况也必须放一个空的文件。

建议交互库的所有需要用户调用的导出函数、以及需要用户定义的函数,都使用 extern "C" 关键字定义,以方便不同语言编写的用户程序调用这些函数。

下面给出如上同一题目的交互库例子:

#include <iostream>

extern "C" {
    extern int plus(int a, int b);
}

int main() {
    int a,b;
    std::cin >> a >> b;
    std::cout << plus(a, b) << std::endl;
    return 0;
}

显然该交互库需要用户定义 plus 函数来实现加法功能。正确的解答可以如此编写:

extern "C" int plus(int a, int b) {
    return a + b;
}

如果希望用户实现的函数调用交互库的函数,可以在 extern "C" 中实现。例如,如果在交互库中有一个 inc() 函数,用于给这个数字增加 1,那么交互库可以这么写:

#include <iostream>

extern "C" {
    extern int plus(int a, int b);
    int inc(int a){
        return a+1;
    }
}

int main() {
    int a,b;
    std::cin >> a >> b;
    std::cout << plus(a, b) << std::endl;
    return 0;
}

正确的解答可以这么写:

extern "C" int inc(int a); // 需要声明这个函数 
extern "C" int plus(int a, int b) {
    int k;
    k = inc(a); // 调用函数
    return k + b - 1; // 其实没啥意义,仅供演示
}
  • 特别需要注意的点

缓冲区问题。C/C++语言的输入输出函数均自带有一定的缓冲区,因此你输出的内容不一定能立刻被交互库/SPJ所读取。如果不在合适的时间清空缓冲区,很可能会造成两个程序互相等待对方输出的情况而 TLE。

每次输出完一定内容、希望对面的程序开始进行处理时,都必须手动清空缓冲区。在 C 语言中,可使用 fflush(stdout) 函数。在C++语言中,可使用 std::cout << std::flush;在使用 std::endl 输出一个换行时,C++语言也会自动清空缓冲区。

交互SPJ最好不要占用太多的 CPU 资源,因为它将与用户的程序在同一个核心上执行。最好在评测程序等待来自SPJ的输入,或者评测程序已经结束运行之后,再进行复杂的计算。


2020-04-17 03:04:03 in 功能说明