对拍

对拍

介绍

当我们写完一段代码但不确定对不对的时候,就可以来个对拍来看看代码的正确性。如果代码有大问题,那马上就可以知道了,但如果代码只是有点小问题,那就要看运气了。

对拍就是用 AC 代码来检查你代码的正确性,那问题来了,我要能 AC 还要对拍干啥。注意,一道题目我们之所以不能写出 AC 代码很大原因是题目的数据范围一般都是 1e5 这个级别的,但我们对拍是可以自己控制数据范围的,所以这时候我们完全可以写一个完全暴力的伪 AC 代码出来,这样就可以对拍了

应用

我们要开四个代码出来,分别命名为 test, ac,data,dp(这里是 C++代码)

test

这个就是我们自己写的代码,直接复制粘贴就可以了

ac

这个就写一个暴力就可以了,但要注意暴力的数据范围,暴力的数据范围就是我们 data.cpp 生成的数据范围

data

这个就是用来生成随机数的

这里有简单的和复杂的两个版本

简单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
int main()
{
srand(time(0));
int n = rand() % 100 + 1;// n 从 1 到 100
cout << n << '\n';
for(int i = 1; i <= n; i++){
int x = rand() % 100 + 1;
cout << x << ' ';
}
cout << '\n';
return 0;
}

这里的核心就是

1
2
srand(time(0));
int n = rand() % 100 + 1;// n 从 1 到 100

srand()用于初始化随机数生成器的“种子”;

time(0) 返回当前系统时间距离 “纪元”(通常是 1970 年 1 月 1 日)的秒数(整数)

这样rand()就可以生成随机数了,再通过取模运算来确定数据范围

rand()的随机性不强,有一定的周期性,同时 rand() 生成的数的范围通常是[0, 32767]

但对于我们对拍是够用了

复杂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<bits/stdc++.h>
using namespace std;
using ll = long long;

int main()
{
random_device rd;
auto now = chrono::system_clock::now().time_since_epoch().count();
mt19937_64 gen(rd() ^ now);
uniform_int_distribution<ll> dist(1, 100);

int n = dist(gen);
cout << n << '\n';
for(int i = 1; i <= n; i++){
int x = dist(gen);
cout << x << ' ';
}
cout << '\n';
return 0;
}

这里核心就是

1
2
3
4
5
random_device rd;
auto now = chrono::system_clock::now().time_since_epoch().count();
mt19937_64 gen(rd() ^ now);
uniform_int_distribution<ll> dist(1, 100);
int n = dist(gen);

random_device rd提供一种初始种子

mt19937_64 gen(rd()); mt19937_64 gen 是一个随机数生成器

正常来说这样就可以生成随机数了,但在 devc++中因为编译器版本问题无法实现随机数

这时就要auto now = chrono::system_clock::now().time_since_epoch().count();来帮忙了

它可以获取当前系统时间距离纪元(epoch)的时间计数值,用它来随机种子就可以了,也就是写成mt19937_64 gen(rd() ^ now);

uniform_int_distribution<ll> dist(1, 100); 用于生成均匀分布的整数,配合我们调整好的 gen 就可以生成随机数了int n = dist(gen)

排列

我们经常会遇到题目数据是一个排列的情况,这时候我们也可以输出一个随机排列出来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<bits/stdc++.h>
using namespace std;
using ll = long long;

int main()
{
random_device rd;
auto now = chrono::system_clock::now().time_since_epoch().count();
mt19937_64 gen(rd() ^ now);
uniform_int_distribution<ll> dist(1, 100);

int n = dist(gen);
cout << n << '\n';
vector<int>perm(n+5);
for(int i = 1; i <= n; i++)perm[i] = i;
shuffle(perm.begin() + 1, perm.begin() + 1 + n, default_random_engine(now));
for(int i = 1; i <= n; i++)cout << perm[i] << ' ';
cout << '\n';
return 0;
}

这里的核心就是 shuffle(perm.begin() + 1, perm.begin() + 1 + n, default_random_engine(now));

shuffle 用于随机重排容器中指定范围的元素,最后一个参数就是随机数引擎,确保排列的随机

dp

这个代码就是把我们的 test,ac,data 代码串起来,达到对拍的作用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include<bits/stdc++.h>
using namespace std;

int main()
{
int t = 0;
while(1){
t++;
system("data.exe > data.in");
system("test.exe < data.in > test.out");
system("ac.exe < data.in > ac.out");
if(system("fc test.out ac.out > diff.log")){
cout << "WA " << t << '\n';
break;
}
cout << "AC" << '\n';
}
return 0;
}

system("data.exe > data.in"); 通过系统命令行运行 data.exe 程序,并把它的输出内容重定向到 data.in 文件里面。通俗的说就是把 data.cpp 的输出复制到 data.in 这样一个 txt 文件里面

system("test.exe < data.in > test.out"); 和上面一样,就是把 data.in 的内容输入到 test.cpp 中,再把 test.cpp 的输出复制到 test.out 这样一个 txt 文件里面

sysytem("ac.exe < data.in > ac.out"); 同理

system("fc test.out ac.out > diff.log"); 这里就是把 test.out ac.out 进行对比,然后把比较结果保存到 diff.log 文件里面

也就是说,我们的 dp.cpp 就是把我们在 data.cpp 生成的随机数输入到 test.cpp(我们写的不确定的代码)与 ac.cpp(我们比赛的时候的小数据暴力代码 或者 赛后找到的大数据 ac 代码)里面,然后把他们的输出进行对比

这里可以把’\n’换成 endl 这样就可以实时看到我们喜欢的 AC,然后一个 WA(qwq)(有时候这样好像也没有用)用 ‘\n’就是一直没有输出,然后所有的 AC 和 WA 一起弹出来,这个可以自己感受一下

这都是小菜,重要的是 WA 的数据,这个当我们的 dp.exe 输出 WA 的时候就可以在文件夹的 data.in 里面看到数据,然后在 diff.log 里面看到正确的输出和我们错误的输出

注意我们文件放的位置,我们的 dp.cpp 只能打开和它同级的文件或文件夹,(同级就是说在一个页面里面的)如下就是都是同级的

111

所以说如果在管理文件的时候exe 文件被放到一个文件夹里,那在system("data.exe > data.in"); 这里要用到exe 文件的地方就要写成system("exe\\data.exe > data.in"); 这里就是我的exe 文件都放到了叫exe 文件夹里面,也就是exe 文件夹是和 dp.cpp 同级的

后文

注意我们的 data.cpp 在面对有多测的题目时候,只要多输出一行 1就可以了

注意我们的 dp.cpp 运行后一直输出 AC,没有 WA 的输出,那也不能说我们的代码一定对了,这时候我们的代码有可能有一点小问题,这是要看生成随机数的运气的,看能不能生成出那个错误的数据

我们可以输出绿色的 AC,红色的 WA,但只能在黑框框里面实现,重定向输出就不行了,下面会在样例的 dp 里面显示

样例

比如题目要输出 a + b 的值, 我们就可以这样写对拍

test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<bits/stdc++.h>
using namespace std;

int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int a, b;
cin >> a >> b;
if(a % 2 && b % 2)cout << a + b + 1 << '\n';
else cout << a + b << '\n';

return 0;
}

ac

1
2
3
4
5
6
7
8
9
10
11
12
#include<bits/stdc++.h>
using namespace std;

int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int a, b;
cin >> a >> b;
cout << a + b << '\n';
return 0;
}

data

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<bits/stdc++.h>
using namespace std;
using ll = long long;

int main()
{
random_device rd;
auto now = chrono::system_clock::now().time_since_epoch().count();
mt19937_64 gen(rd() ^ now);
uniform_int_distribution<ll> dist(1, 100);
//cout << 1 << '\n'; // 如果有多测的话
int n = dist(gen);
int m = dist(gen);
cout << n << ' ' << m << '\n';
return 0;
}

dp

注意上文说的文件位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include<bits/stdc++.h>
using namespace std;
#define red "\033[31m"
#define green "\033[32m"
#define reset "\033[0m"

int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int t = 0;
while(1){
t++;
system("data.exe > data.in");
system("test.exe < data.in > test.out");
system("ac.exe < data.in > ac.out");
if(system("fc test.out ac.out > diff.log")){
cout << red << "WA " << t << reset << '\n';
break;
}
cout << green << "AC" << '\n';
}
return 0;
}

这样运行 dp.cpp 后就会在某一个时刻输出 WA 并停止,这时候我们打开我们代码存放的文件夹,然后打开 data.in 就可以看到数据,diff.log 里面看到我们的输出和正确输出

  • 本文作者: kass
  • 本文链接: http://example.com/2025/09/10/对拍/
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!