upgrade to python3;add some validator examples
This commit is contained in:
328
examples/custom_validators.md
Normal file
328
examples/custom_validators.md
Normal file
@@ -0,0 +1,328 @@
|
||||
# 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 = '''
|
||||
// 这里放置验证程序的源代码
|
||||
'''
|
||||
```
|
||||
Reference in New Issue
Block a user