Files
bitoj_python/examples/custom_validators.md

328 lines
8.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# BitOJ 自定义验证程序示例
## 1. 基本验证程序结构
自定义验证程序接收以下参数:
- 标准输入文件
- 标准输出文件(期望答案)
- 用户输出文件(用户程序的输出)
验证程序需要输出以下结果之一:
- `AC` - 答案正确
- `PE` - 格式错误(如果支持)
- `WA` - 答案错误
## 2. 示例1数值误差比较验证程序C++
适用于需要浮点数比较的题目:
```cpp
#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
适用于有多个正确答案的题目:
```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++
验证最短路径问题,检查路径是否合法且最优:
```cpp
#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
验证输出格式是否正确:
```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++
模拟交互过程:
```cpp
#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;
}
```
## 使用方法
1. **编写验证程序**:选择合适的编程语言编写验证逻辑
2. **设置题目**:在题目配置中指定使用自定义验证程序
3. **上传验证代码**:将验证程序代码保存到系统中
4. **测试验证**:确保验证程序能正确处理各种情况
## 注意事项
1. **异常处理**:验证程序必须处理所有可能的异常情况
2. **性能考虑**:验证程序也有时间限制,需要高效实现
3. **输出格式**:验证程序必须严格按照 AC/PE/WA 格式输出
4. **安全性**:验证程序在沙箱环境中运行,有资源限制
5. **可移植性**:确保验证程序在评测环境中能正常编译运行
## 配置示例
在 BitOJ 中配置自定义验证程序:
```python
# 在题目配置中设置自定义验证程序
problem.validator_language = 'gcc-3.3' # 验证程序语言
problem.validator_code = '''
// 这里放置验证程序的源代码
'''
```