8.0 KiB
8.0 KiB
BitOJ 自定义验证程序示例
1. 基本验证程序结构
自定义验证程序接收以下参数:
- 标准输入文件
- 标准输出文件(期望答案)
- 用户输出文件(用户程序的输出)
验证程序需要输出以下结果之一:
AC- 答案正确PE- 格式错误(如果支持)WA- 答案错误
2. 示例1:数值误差比较验证程序(C++)
适用于需要浮点数比较的题目:
#include <iostream>
#include <fstream>
#include <cmath>
#include <string>
using namespace std;
const double EPS = 1e-9;
int main(int argc, char* argv[]) {
// argv[1]: 标准输入文件
// argv[2]: 标准输出文件
// argv[3]: 用户输出文件
ifstream expected(argv[2]); // 标准答案
ifstream result(argv[3]); // 用户输出
double exp_val, res_val;
// 读取期望的浮点数
if (!(expected >> exp_val)) {
cout << "WA" << endl;
return 0;
}
// 读取用户输出的浮点数
if (!(result >> res_val)) {
cout << "WA" << endl;
return 0;
}
// 比较数值,允许误差
if (fabs(exp_val - res_val) <= EPS) {
cout << "AC" << endl;
} else {
cout << "WA" << endl;
}
return 0;
}
3. 示例2:多解答案验证程序(Python)
适用于有多个正确答案的题目:
#!/usr/bin/env python
import sys
def main():
# sys.argv[1]: 标准输入文件
# sys.argv[2]: 标准输出文件
# sys.argv[3]: 用户输出文件
try:
with open(sys.argv[2], 'r') as f:
expected = f.read().strip()
with open(sys.argv[3], 'r') as f:
result = f.read().strip()
# 假设这是一个排序题目,任何正确的排序都是对的
expected_nums = list(map(int, expected.split()))
result_nums = list(map(int, result.split()))
# 检查数量是否相同
if len(expected_nums) != len(result_nums):
print("WA")
return
# 检查排序后是否相同(原始数据相同)
if sorted(expected_nums) != sorted(result_nums):
print("WA")
return
# 检查用户输出是否有序
if result_nums == sorted(result_nums):
print("AC")
else:
print("WA")
except Exception as e:
print("WA")
if __name__ == "__main__":
main()
4. 示例3:图论路径验证程序(C++)
验证最短路径问题,检查路径是否合法且最优:
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <sstream>
using namespace std;
struct Edge {
int to, weight;
};
int main(int argc, char* argv[]) {
ifstream input(argv[1]); // 输入数据
ifstream expected(argv[2]); // 期望答案
ifstream result(argv[3]); // 用户答案
// 读取图的信息
int n, m;
input >> n >> m;
vector<vector<Edge>> graph(n + 1);
for (int i = 0; i < m; i++) {
int u, v, w;
input >> u >> v >> w;
graph[u].push_back({v, w});
graph[v].push_back({u, w}); // 无向图
}
int start, end;
input >> start >> end;
// 读取期望的最短距离
int expected_dist;
expected >> expected_dist;
// 读取用户输出的路径
string line;
getline(result, line);
istringstream iss(line);
vector<int> path;
int node;
while (iss >> node) {
path.push_back(node);
}
// 验证路径
if (path.empty() || path[0] != start || path.back() != end) {
cout << "WA" << endl;
return 0;
}
// 计算路径长度
int total_dist = 0;
for (int i = 0; i < path.size() - 1; i++) {
int u = path[i], v = path[i + 1];
bool found = false;
for (const Edge& e : graph[u]) {
if (e.to == v) {
total_dist += e.weight;
found = true;
break;
}
}
if (!found) {
cout << "WA" << endl; // 路径不存在
return 0;
}
}
// 检查是否是最短路径
if (total_dist == expected_dist) {
cout << "AC" << endl;
} else {
cout << "WA" << endl;
}
return 0;
}
5. 示例4:字符串格式验证程序(Java)
验证输出格式是否正确:
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
// args[0]: 标准输入文件
// args[1]: 标准输出文件
// args[2]: 用户输出文件
Scanner expected = new Scanner(new File(args[1]));
Scanner result = new Scanner(new File(args[2]));
try {
// 读取期望的行数
int expectedLines = expected.nextInt();
// 检查用户输出的行数
int actualLines = 0;
List<String> userLines = new ArrayList<>();
while (result.hasNextLine()) {
String line = result.nextLine().trim();
if (!line.isEmpty()) {
userLines.add(line);
actualLines++;
}
}
if (actualLines != expectedLines) {
System.out.println("WA");
return;
}
// 检查每行格式:必须是 "Case #X: Y" 的格式
for (int i = 0; i < userLines.size(); i++) {
String line = userLines.get(i);
String pattern = "Case #" + (i + 1) + ": \\d+";
if (!line.matches(pattern)) {
System.out.println("PE"); // 格式错误
return;
}
}
System.out.println("AC");
} catch (Exception e) {
System.out.println("WA");
} finally {
expected.close();
result.close();
}
}
}
6. 示例5:交互式题目验证程序(C++)
模拟交互过程:
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
using namespace std;
int main(int argc, char* argv[]) {
ifstream input(argv[1]); // 输入数据
ifstream expected(argv[2]); // 期望的查询次数等
ifstream result(argv[3]); // 用户的查询序列
// 读取隐藏的数字
int secret;
input >> secret;
// 读取最大查询次数
int maxQueries;
expected >> maxQueries;
// 模拟交互过程
int queryCount = 0;
int guess;
bool found = false;
while (result >> guess && queryCount < maxQueries) {
queryCount++;
if (guess == secret) {
found = true;
break;
}
// 在实际交互中,这里会给用户反馈 "too high" 或 "too low"
}
// 检查结果
if (found && queryCount <= maxQueries) {
cout << "AC" << endl;
} else {
cout << "WA" << endl;
}
return 0;
}
使用方法
- 编写验证程序:选择合适的编程语言编写验证逻辑
- 设置题目:在题目配置中指定使用自定义验证程序
- 上传验证代码:将验证程序代码保存到系统中
- 测试验证:确保验证程序能正确处理各种情况
注意事项
- 异常处理:验证程序必须处理所有可能的异常情况
- 性能考虑:验证程序也有时间限制,需要高效实现
- 输出格式:验证程序必须严格按照 AC/PE/WA 格式输出
- 安全性:验证程序在沙箱环境中运行,有资源限制
- 可移植性:确保验证程序在评测环境中能正常编译运行
配置示例
在 BitOJ 中配置自定义验证程序:
# 在题目配置中设置自定义验证程序
problem.validator_language = 'gcc-3.3' # 验证程序语言
problem.validator_code = '''
// 这里放置验证程序的源代码
'''