Files
bitoj_python/examples/README_自定义验证程序.md

6.8 KiB
Raw Permalink Blame History

BitOJ 自定义验证程序完整指南

概述

BitOJ系统支持4种判题方式

  1. 字符串精确比较 - 逐字符对比输出
  2. 字符串容错比较 - 允许格式错误(PE),主要比较数字内容
  3. 文件MD5校验 - 比较文件大小和MD5值
  4. 自定义验证程序 - 编写专门的验证逻辑

本文档重点介绍第4种方式自定义验证程序的编写和使用。

自定义验证程序原理

工作流程

  1. 系统执行用户提交的代码
  2. 将输入、期望输出、实际输出传递给验证程序
  3. 验证程序分析这些文件并返回判题结果
  4. 系统根据返回结果确定最终评分

参数说明

验证程序接收以下参数:

  • argv[1]: 标准输入文件路径
  • argv[2]: 期望输出文件路径
  • argv[3]: 用户程序输出文件路径

返回值

验证程序必须输出以下结果之一:

  • AC - Accepted答案正确
  • PE - Presentation Error格式错误
  • WA - Wrong Answer答案错误

实际示例

1. 浮点数误差比较 (C++)

适用场景: 计算几何、数值计算等需要浮点数比较的题目

#include <iostream>
#include <fstream>
#include <cmath>
using namespace std;

const double EPS = 1e-9;

int main(int argc, char* argv[]) {
    ifstream expected(argv[2]);  // 标准答案
    ifstream result(argv[3]);    // 用户输出
    
    double exp_val, res_val;
    if (!(expected >> exp_val) || !(result >> res_val)) {
        cout << "WA" << endl;
        return 0;
    }
    
    if (fabs(exp_val - res_val) <= EPS) {
        cout << "AC" << endl;
    } else {
        cout << "WA" << endl;
    }
    
    return 0;
}

2. 多解答案验证 (Python)

适用场景: 排序题、图论路径题等有多种正确答案的题目

#!/usr/bin/env python
import sys

def main():
    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 = sorted(map(int, expected.split()))
        result_nums = sorted(map(int, result.split()))
        
        if expected_nums == result_nums:
            print("AC")
        else:
            print("WA")
    except:
        print("WA")

if __name__ == "__main__":
    main()

3. 输出格式验证 (Java)

适用场景: 需要严格输出格式的竞赛题目

import java.io.*;
import java.util.*;
import java.util.regex.Pattern;

public class StringFormatValidator {
    public static void main(String[] args) {
        try {
            Scanner result = new Scanner(new File(args[2]));
            
            int lineNum = 1;
            while (result.hasNextLine()) {
                String line = result.nextLine().trim();
                
                // 检查格式Case #X: Y
                Pattern pattern = Pattern.compile("^Case #" + lineNum + ": \\d+$");
                if (!pattern.matcher(line).matches()) {
                    System.out.println("PE");
                    return;
                }
                lineNum++;
            }
            
            System.out.println("AC");
        } catch (Exception e) {
            System.out.println("WA");
        }
    }
}

在BitOJ中配置自定义验证程序

代码配置示例

from judgescript import ExternalJudge

# 创建自定义验证程序
validator = ExternalJudge(
    problemid='P1001',           # 题目ID
    vlang='gcc-3.3',            # 验证程序语言
    vcode=validator_code         # 验证程序源代码
)

# 在测试过程中调用
result = validator.judge(
    sid='S123',                  # 提交ID
    tid='T001',                  # 测试用例ID
    tin='/path/to/input.txt',    # 输入文件
    tout='/path/to/output.txt',  # 期望输出
    result='/path/to/result.txt', # 实际输出
    errfile='/path/to/error.txt' # 错误输出
)

支持的编程语言

语言 标识符 说明
C gcc-3.3 适合高性能验证
C++ g++-3.3 支持STL容器
Java java-1.6 强类型安全
Python python-2.5 快速开发
Pascal fpc-2.2 简单逻辑

最佳实践

1. 错误处理

// 文件读取失败处理
if (!input_file.is_open()) {
    cout << "WA" << endl;
    return 0;
}

// 数据解析失败处理
try {
    value = stod(line);
} catch (const exception& e) {
    cout << "WA" << endl;
    return 0;
}

2. 性能优化

  • 使用适当的数据结构
  • 避免不必要的字符串操作
  • 合理设置缓冲区大小
  • 注意内存使用限制

3. 安全考虑

  • 验证输入参数数量
  • 处理异常文件路径
  • 限制递归深度
  • 避免无限循环

4. 可维护性

  • 添加清晰的注释
  • 使用有意义的变量名
  • 模块化验证逻辑
  • 提供调试输出选项

常见应用场景

1. 数值计算题

  • 浮点数精度比较
  • 矩阵运算结果验证
  • 统计数值的近似比较

2. 图论算法题

  • 最短路径验证(允许多条等长路径)
  • 最小生成树验证(权重相等即可)
  • 图遍历结果验证

3. 字符串处理题

  • 忽略大小写比较
  • 忽略空白字符差异
  • 正则表达式匹配

4. 组合数学题

  • 排列组合结果验证
  • 集合操作结果比较
  • 数学证明步骤检查

5. 交互式题目

  • 模拟交互过程
  • 验证查询次数限制
  • 检查答案正确性

调试技巧

1. 本地测试

# 编译验证程序
g++ -o validator validator.cpp

# 准备测试文件
echo "3.14159" > expected.txt
echo "3.14160" > result.txt

# 运行测试
./validator input.txt expected.txt result.txt

2. 日志输出

#ifdef DEBUG
    cerr << "Expected: " << exp_val << ", Got: " << res_val << endl;
    cerr << "Difference: " << fabs(exp_val - res_val) << endl;
#endif

3. 边界情况测试

  • 空输出文件
  • 超大数值
  • 特殊字符
  • 格式错误的输入

故障排除

常见问题

  1. 编译失败

    • 检查语言版本兼容性
    • 确认头文件引用正确
    • 验证语法错误
  2. 运行超时

    • 优化算法复杂度
    • 减少不必要的计算
    • 检查是否有死循环
  3. 内存不足

    • 减少内存分配
    • 及时释放资源
    • 使用流式处理
  4. 判题结果异常

    • 验证输出格式
    • 检查返回值规范
    • 确认异常处理逻辑

总结

自定义验证程序是BitOJ系统最灵活的判题方式能够处理各种复杂的判题需求。通过合理设计验证逻辑可以支持

  • 多解答案题目
  • 浮点数精度要求
  • 特殊输出格式
  • 交互式评测
  • 复杂的正确性检查

掌握自定义验证程序的编写技巧,能够大大扩展在线评测系统的应用范围,为各种类型的编程竞赛和教学需求提供支持。