交互题功能说明
交互题即用户提交的程序,通过出题人提供的交互库,与判题程序进行交互并获得输入、解答问题。
洛谷上的交互题评测,由以下三个部分组成:判题程序(评测机或者 SPJ)、交互库、用户程序。其中出题人需要提供交互库,并且根据试题需求可能需要 SPJ。交互题需要打上 交互题 标签。如果使用 SPJ 功能,还需要 Special Judge 标签。
在数据压缩包中,除了测试点输入输出文件,还需包含 interactive_lib.cpp 交互库文件。交互库与 SPJ 相同,均使用 C++14 标准进行编译,不支持其他语言。
函数式交互题
函数式 交互题是一种题目类型,其中参赛者的程序需要与判题系统进行交互,但这种交互的方式通常是通过函数调用的形式来实现,而不是通过标准输入输出流进行交互。也就是说,选手的程序与系统的交互是通过一组预定义的函数接口来进行的,而不是通过输入输出文本。
无需使用 SPJ 的情况
以 P13691 [CEOI 2025] highest 为例,本题需要用户实现以下函数:
vector<int> solve(vector<int> &v, vector<int> &w, vector<pair<int,int>> &queries);
那么本题的交互库所需要承担的行为是:
- 将输入数据读入进交互库;
- 调用
vector<int> solve(vector<int> &v, vector<int> &w, vector<pair<int,int>> &queries);,此时选手程序运行并且返回结果; - 输出结果,与答案文件进行比较;
换而言之,这类试题中,交互库的本质是帮助选手写完输入输出,选手只需编写核心程序。因此,它的交互库可以写作:
#include <iostream>
#include <vector>
using namespace std;
vector<int> solve(vector<int> &v, vector<int> &w, vector<pair<int, int>> &queries);
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<int> v(n);
for (int i = 0; i < n; i++) {
cin >> v[i];
}
vector<int> w(n);
for (int i = 0; i < n; i++) {
cin >> w[i];
}
int m;
cin >> m;
vector<pair<int, int>> queries(m);
for (int i = 0; i < m; i++) {
cin >> queries[i].first >> queries[i].second;
}
vector<int> results = solve(v, w, queries);
for (int i = 0; i < m; i++) {
cout << results[i] << "\n";
}
fflush(stdout);
return 0;
}
在文件目录下仅需 interactive_lib.cpp 即可,无需添加 checker.cpp,只需勾选交互题标签。
需要使用 SPJ 的情况
事实上,大部分函数式交互题目并不像上例那么简单。以 P13612 [IOI 2018] combo 组合动作 为例,本题需要选手实现函数 string guess_sequence(int N),允许选手调用函数 int press(string p),且根据函数调用次数反馈评分。这个时候需要用到 Special Judge 功能。在文件目录下需要 interactive_lib.cpp 和 checker.cpp,并且同时勾选交互题和 Special Judge 标签。
在此类情况下,测试数据在 checker、interactive_lib 的流转如下:
- 从 checker 的 inf 流读取试题原本的测试数据,再使用 stdout 输出信息。
- 此时,interactive_lib 可以从 stdin 输入 checker 输出的信息,进行处理,然后从 interactive_lib 的 stdout 输出信息。
- 接着,checker 从 stdin(或者 ouf 流)获得 interactive_lib 输出的信息,进行处理。
- 最后,checker 根据函数调用次数等信息,为选手的程序评分。
以上述试题为例,在测试数据内存放的是字符串 ,因此需要 checker 从 inf 流内输入 。由于题目要求选手只应知道字符串的长度 ,因此需要将字符串 的长度输出给交互库。
// 这里是在 checker 内的行为
S = inf.readLine(); // 从输入文件读入 S
N = S.length();
std::cout << N << std::endl; // 将 S 输出给 interactive_lib