题目数据合法性检验
2025-01-11 01:29:23
发布于:加拿大
本文将简要介绍在竞赛与工程场景中,如何进行数据合法性检验,并分别展示在 C++ 中使用 cassert
、使用 testlib 框架,以及在 Python 中使用内置的 assert
关键字进行数据校验的基本方法。文中所有示例的代码风格均与之前分享的示例风格相似,力求简洁易读。
一、为什么要进行数据合法性检验
在命题和解题场景中,确保输入数据符合题目要求是非常关键的。若输入数据出现非法值(例如越界、格式错误、非法字符等),会导致后续的算法逻辑失效,甚至程序崩溃。为了保证测试数据的准确性,往往需要在提交前(或评测中)就对输入数据做严格的合法性检验。
常见需要检验的地方:
- 整数的范围:例如
1 <= T <= 100
,-10000 <= A[i] <= 10000
等。 - 字符串的格式:是否包含非法字符、大小写是否符合要求、长度是否超限等。
- 数组或列表的大小:输入的数组大小与预期是否一致,或者剩余的输入行数是否满足需要。
- 组合条件:多条数据之间的逻辑是否符合,如
L <= R
,abs(a - c) + abs(b - d) == 1
等。
二、使用 C++ 的 cassert 进行数据校验
1. 基本示例
C++ 中的 cassert
头文件提供了一个简单的断言机制 assert(condition)
,在编译时若开启断言(默认 Debug 模式开启),当 condition
为 false
时,程序会直接退出并抛出一条错误信息,提示断言失败。
下面以一个简化的示例展示如何使用 cassert
验证输入范围。假设我们想要检验有 T
组数据,每组数据给出一个整数 x
,并保证 1 <= T <= 100
且 1 <= x <= 10^8
:
#include <bits/stdc++.h>
#include <cassert>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
assert(1 <= T && T <= 100);
while (T--) {
long long x;
cin >> x;
assert(1 <= x && x <= 100000000); // 10^8
}
cout << "Testcases are valid." << "\n";
return 0;
}
- 在本示例中,如果
T
或任意x
超出指定范围,程序会立即触发断言错误并退出,从而保证数据的合法性。
三、使用 testlib 进行数据校验
testlib 是 Codeforces 出品的测试数据生成与校验框架,常用于创建和校验测试数据文件。
1. 典型用法
一般在校验脚本中(验证器),使用方式如下(示例风格与官网文档相似,但逻辑简化):
#include "testlib.h"
#include <bits/stdc++.h>
using namespace std;
int main(int argc, char* argv[]) {
registerValidation(argc, argv);
// 读取 T, 范围 1~100
int T = inf.readInt(1, 100, "T");
inf.readEoln();
for (int i = 0; i < T; i++) {
// 读取一个整数 x, 范围 1~10^8
long long x = inf.readLong(1, 100000000, "x");
inf.readEoln();
}
inf.readEof();
return 0;
}
inf.readInt(1, 100, "T")
会自动校验输入是否在[1, 100]
范围内,否则验证器会抛出错误并停止校验。inf.readEoln()
用于读取并检验当前行末尾是否存在额外字符,确保输入行的格式正确。
2. testlib 的优势
- 自动化:无需手写
assert
或if
条件语句,阅读与维护更加方便。 - 详细错误信息:当出现越界或格式错误时,会给出精确的报错行数和提示信息,便于快速排查问题。
- 完备的读取方法:对于整数、实数、字符串都有对应的读取与检验方法,包括严格的格式检查。
四、使用 Python 的内置 assert 进行数据校验
对于 Python 的数据校验,最常见的做法是直接使用内置的 assert
关键字。
一旦断言失败,会抛出 AssertionError
并停止程序运行。
1. 基本用法
下面是之前展示过的若干个题目的 Python 校验示例,它们都遵循了一个相似的模式:
- 首先读取需要的参数(如
T
、n
、m
等)。 - 使用
assert
检查该参数是否在指定范围。 - 依次读取数据并对每条数据做对应的断言。
以下为几个常见场景的示例——从简单的整型范围校验、字符串校验到多维数组及综合逻辑检验。
T1 - 纪元流星雨
T = int(input())
assert 1 <= T <= 100
for i in range(T):
B, L, E = map(int, input().split())
assert 1 <= B <= 10 ** 8
assert 20 <= L <= 10 ** 8
assert 0 <= E <= 49
print("Testcases are valid.")
- 这里我们验证了
T
的范围;每组输入中(B, L, E)
也都进行了相应的断言。 - 最后一行
"Testcases are valid."
表示所有断言通过后才会正常输出。
T2 - MARCOncatenation
T = int(input())
assert 1 <= T <= 100
for i in range(T):
string = input()
assert string.lower() == string
assert string.isalpha()
assert 1 <= len(string) <= 500
print("Testcases are valid.")
- 在这里我们对字符串内容进行了检查:
string.lower() == string
确保字符串为全小写;string.isalpha()
确保字符串都是字母;- 字符串长度限制。
T3 - TNT接力
T = int(input())
assert 1 <= T <= 100
for i in range(T):
N, M = map(int, input().split())
assert 1 <= N <= 10000
assert 1 <= M <= 10000
string = input()
assert len(string) == N
assert string.count("-") + string.count("#") == N
print("Testcases are valid.")
- 对于长度固定的字符串,可以用
assert len(string) == N
检验; - 检查字符串中的某些特殊字符出现次数能否与总长匹配。
T4 - 小丑牌
T = int(input())
assert 1 <= T <= 10 ** 5
numSet = ["J", "Q", "K", "A"]
for i in range(2, 11):
numSet.append(str(i))
rankSet = ["Spade", "Heart", "Diamond", "Club"]
for testcase in range(T):
input()
for i in range(5):
num, rank = input().split()
assert num in numSet
assert rank in rankSet
print("Testcases are valid.")
- 此例较为典型:先构造
numSet
和rankSet
,然后对牌的数字与花色进行严格校验。 T
的范围较大,达到了10^5
,同样使用assert
即可。
T5 - Vertex Verse
n, m, q = map(int, input().split())
assert 1 <= n <= 500
assert 1 <= m <= 500
assert 1 <= q * 2 <= min(10 ** 4, n * (n - 1) + m * (m - 1))
for i in range(q):
for j in range(2):
a, b, c, d = map(int, input().split())
assert 1 <= a <= n
assert 1 <= b <= m
assert 1 <= c <= n
assert 1 <= d <= m
assert abs(a - c) + abs(b - d) == 1
print("Testcases are valid.")
- 这里的重点是
abs(a - c) + abs(b - d) == 1
,也就是确保位置间仅有一步之遥。 - 此类“多次读入 + 组合条件”非常常见,可以把多行检查逻辑封装在循环中。
T6 - 最优政府大楼选址 - 2
N = int(input())
assert 1 <= N <= 5000
E = list(map(int, input().split()))
assert len(E) == N
for i in E:
assert 1 <= i <= 10
for i in range(N):
xi, yi = map(float, input().split())
assert 0 <= abs(xi) <= 10 ** 3
assert 0 <= abs(yi) <= 10 ** 3
print("Testcases are valid.")
- 在这里我们需要读取一整行整数并进行批量校验,然后再读入浮点数并逐个校验绝对值是否在
10^3
内。
T7 - 乌龟养殖场
N, M = map(int, input().split())
assert 1 <= N <= 500
assert 1 <= M <= 10
for i in range(N):
row = list(map(int, input().split()))
assert row.count(0) + row.count(1) == M
print("Testcases are valid.")
- 需要保证输入矩阵里只含有
0
或1
,并且列数为M
。 - 通过
row.count(0) + row.count(1) == M
的断言,简洁地验证了矩阵格式。
T8 - 数据中心能耗分析
N, M = map(int, input().split())
assert 1 <= N <= 10 ** 5
assert 1 <= M <= 10 ** 5
A = list(map(int, input().split()))
for i in A:
assert -10000 <= i <= 10000
assert len(A) == N
for i in range(M):
op = list(map(int, input().split()))
assert op[0] == 1 or op[0] == 2
if op[0] == 1:
T, L, R, K = op
assert 1 <= L and L <= R and R <= N
assert -1000 <= K <= 1000
else:
T, L, R = op
assert 1 <= L and L <= R and R <= N
print("Testcases are valid.")
这里空空如也
有帮助,赞一个