目录

本文将会从以下方面讲解:

  1. 对拍的概念
  2. 对拍的作用
  3. 对拍的用法
  4. 总结
  5. 后记

概念

首先,我们要明确一下对拍是什么,相信广大 OIer 都听过对拍的大名,那像我们这些蒟蒻(无恶意,纯自我调侃)就可能还不太完全理解对拍的概念以及作用,相信大家也都听过说“会了对拍,J 组前两题就能过”,真的是这样吗?本蒟蒻在这里普及一下:

  • 对拍并不是一种算法,而只是一种用于检测你程序正确性的方法,这个方法要求你会打暴力(这个我相信大家都可以)、会打自己眼中的正解(说明也需要算法等基础);
  • 对拍需要四个代码:你认为的正解、一定正确的暴力、随机数据生成、检测函数,接下来本人将分成多个方面进行讲解。

作用

看了概念的蒟蒻们都知道,其实对拍就是检验自己赛时代码的正确性的,仅此而已,并不是什么深奥难懂的东西。

实现

前面两个都是一些理论层面的详解,而现在要开始实操了(下文以 A + B Problem 为例讲解,其余题目因题而异)。

首先我们要准备 4 个 cpp 文件(上文提及过),前两个容易理解,后两个蒟蒻们需要理解一下,可能有点难。

注:本方法介绍的方法无需在代码内写 freopen。

  • 自己的代码,待验证的自己眼中的正解
#include <bits/stdc++.h>
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define in(x) scanf ("%lld",&x)
#define out1(x) printf ("%lld ",x)
#define out2(x) printf ("%lld\n",x)
#define debug(x) printf ("debug: %lld\n",x)
using namespace std;
signed main() {
	int a,b;
	in(a);in(b);
	out2(a+b);
	
	return 0;
}
  • 暴力代码,以累加为例
#include <bits/stdc++.h>
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define in(x) scanf ("%lld",&x)
#define out1(x) printf ("%lld ",x)
#define out2(x) printf ("%lld\n",x)
#define debug(x) printf ("debug: %lld\n",x)
using namespace std;
signed main() {
	int a,b;
	in(a);in(b);
	int ans=0;
	for (int i=0;i<a;i++)ans++;
	for (int i=0;i<b;i++)ans++;
	out2(ans);
	return 0;
}
  • 随机生成代码
#include <bits/stdc++.h>
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define in(x) scanf ("%lld",&x)
#define out1(x) printf ("%lld ",x)
#define out2(x) printf ("%lld\n",x)
#define debug(x) printf ("debug: %lld\n",x)
using namespace std;
const int L=1; // 下界
const int R=1e6; // 上界
// 上下界决定:生成区间的范围,也就是(L,R)
mt19937 plant(time(nullptr)+random_device{}()); // mt19937是一个十分优质的随机数种子,而这里就是生成一个随机数种子
uniform_int_distribution<> dis(L,R); // 确定数据范围
#define get dis(plant) // 调用方法 dis(种子)
signed main() {
	int a=get,b=get;
	out1(a);out2(b);
	return 0;
}

注释已经标的比较清楚了,自己理解一下,如果实在不行,就死记硬背生成模板

  • 最重要的,检查函数

这个函数先说概念:检测暴力的输出与己代码的输出是否相同

(注:网上有 bat 文件写法,我为什么不推荐,具体原因在后记)

// 代码内使用了cmd命令
/*
rand.exe就是随机数生成
data.exe就是暴力代码
me.exe就是待检测代码
make.in就是随机生成的输入
test.ans / out分别是暴力和己代码的输出
其实理解cmd命令可以根据箭头方向来理解
*/
#include <bits/stdc++.h>
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define in(x) scanf ("%lld",&x)
#define out1(x) printf ("%lld ",x)
#define out2(x) printf ("%lld\n",x)
#define debug(x) printf ("debug: %lld\n",x)
using namespace std;
signed main() {
	int cnt=0; // 检测了多少个文件
	while(1) {
		out2 (++cnt); // 输出当前到了第几个对拍文件,当数量够多都没报错时,就可以看看要不要关掉了
		system("rand.exe > make.in"); // 运行生成数据代码,将输出存到make.in里(其实就是生成数据)
		system("data.exe < make.in > test.ans"); // 运行暴力代码,从随机生成的数据里读取输入,将输出存到test.ans里
		system("me.exe < make.in > test.out"); // 与暴力代码同理
		if(system("fc test.out test.ans")) { // 死记硬背就行了,fc是判断内容是否相同,相同返回0,不同返回1
			printf("This in is error\n");
			break ; // 找到特例,退出,当前make.in里的输入就是特例
		}
	}
	
	return 0;
}
  • Win 11 实测画面

这是正常无特例的时候

这是有特例的时候

  • 注意事项
  1. 所有对拍要用的文件都要保存在同目录(这里建议建一个文件夹,全部存进去)下
  2. 如上图所示,记得将数据生成、暴力、你的代码都先运行一遍,使其产生 exe ,在运行检测函数就可以了,重构代码后,也要重新运行,确保 exe 是最新的

总结

对拍终究只是一种检验方法,写代码时仍需要扎实的功底,所以对拍也只是你的辅助,最重要的是你的努力

最后祝大家在 2025 CSP 中取得理想的成绩

后记

你们去查阅网上资料,可能会看见 check(检测函数)是用 bat 文件写的,代码如下:

: loop
rand.exe
data.exe
me.exe
fc test.ans test.out
if not erroelevel1 goto loop

但为什么我这里没有推荐呢?因为在我的实测下来,bat 文件在运行时会提示缺少各种 dll(动态链接库),如下图: 这是为什么呢?

首先你需要知道 dll 是什么,每个应用程序在下载时就配置好了他,但 dll 仅限这个程序使用,而 bat 文件并不会继承你 IDE 配置好的 dll;而相反,exe 因为它是由 cpp 编译的,cpp 有是由 IDE 编译的,所以他会继承 dll 库,从而成功运行。

1 条评论

  • @ 2025-10-28 17:30:07

    这时候,gzy发话了:“不如我的随机数

  • 1

本篇文章被评分为:

100