浅谈c++实现对拍

论如何对拍

1 用途(需求)

赛场或平时验证某一程序的正确性。

2 基本要求

  • 不需要写文操!!!
  • 必须能够确保能够写出一个输出正确答案的std.exe程序(不要求时间)。
  • 适用于算法优化暴力复杂度或验证dp正确性。
  • 不适用于模拟题。
  • 读入不宜过于复杂。
  • 本博客中所有程序要求处于同一文件夹。

3 基本实现

3.1 写出std.exe文件

这个我不会教,也教不了、、
注意事项

  • 确保正确性!!!
  • 不需考虑时间复杂度。

3.2 制造定数量级的随机数

3.2.1 手造随机数前置基础

首先须知rand()函数。
最重要的一点: r a n d ( ) ∈ [ 1 , 32767 ] rand()\in[1,32767] rand()[1,32767]
因此,当我们想要造 1 0 9 10^9 109甚至 1 0 18 10^{18} 1018级别的随机数时,需手写 r a n d ( ) rand() rand()函数。

3.2.2 学会手造 1 0 9 10^9 109数量级的随机数

随机数范围 [ a , b ] [a,b] [a,b]

#define int long long

int rand9(int a, int b) {
	int res = 0;
	int t = rand();//最大只能至32767
	for (int i = 1; i <= t; ++i) 
		res = (res + rand()) % (b - a + 1) + a;
	return res;
}

各位可以在本地跑一下。
(是不是发现它只输出一个定值)
但是如果在主函数加一个srand(time(0))呢?

#include 
#define int long long
using namespace std;

int rand9(int a, int b) {
	int res = 0;
	int t = rand();//最大只能至32767
	for (int i = 1; i <= t; ++i) 
		res = (res + rand()) % (b - a + 1) + a;
	return res;
}

signed main() {
	srand(time(0));
	printf("%lld\n", rand9(1, 1000000000));
	return 0;
}

再次尝试运行,发现趋于某个常数,但不稳定。
(不必要计算回归)

3.2.3 学会手造 1 0 18 10^{18} 1018数量级的随机数

同理。

#include 
#define int long long
using namespace std;

int rand9(int a, int b) {
	int res = 0;
	int t = rand();//最大只能至32767
	for (int i = 1; i <= t; ++i) 
		res = (res + rand()) % (b - a + 1) + a;
	return res;
}

int rand18(int a, int b) {
	int res = 0;
	int t = rand9(a, b);//最大只能至1073676289 
	for (int i = 1; i <= t; ++i) 
		res = (res + rand9(a, b)) % (b - a + 1) + a;
	return res;
}

signed main() {
	srand(time(0));
	printf("%lld\n", rand18(1, 1000000000));
	return 0;
}
3.2.4 更为简易且更为随机的随机数

从c++11开始c++多了一个新的STLmt19937
其中mt m a x i n t maxint maxint
19937指其周期,为 2 19937 − 1 2^{19937}-1 2199371
当我看到这个东西的时候吓我一跳(确信)。
这个函数有一下几点好处:

  • 速度快。
  • 周期奇长无比。
  • 特别好写!!!

有了这个函数,我们就可以快速完成 1 0 9 10^9 109数量级随机数的制造。

#include 
#define int long long
using namespace std;

mt19937 rnd(time(0));

signed main() {
	printf("%lld\n", rnd());
	return 0;
} 

1 0 18 10^{18} 1018rand()函数 1 0 9 10^9 109的造法。

3.2.5 大佬习惯的造法

这里直接贴代码。

#include 
#define int long long
using namespace std;

mt19937 rnd(time(0));

signed main() {
	printf("%lld\n", rnd() * rnd());
	return 0;
}

3.3 造出样例

注意观察特殊要求!!!

3.3.1 基础样例

对于一个或多个无相关的读入直接输出随机数。

3.3.2 读入数列

若有数据要求,需要排序。

3.4 写出对拍程序

以下对拍简称dp(个人习惯)。
这个没什么好讲的。
可以了解一下windows.h头文件与该头文件下system函数。

#include 

int main() {
	while (1) {
		system("sample.exe > 文件名.in");
		system("文件名.exe < 文件名.in > 文件名.out");
		system("std.exe < 文件名.in > 文件名.ans");
		if (system("fc 文件名.out 文件名.ans"))
			break;
	}
	return 0;
}

或者这样:

#include 
#include 

long long cnt;

int main() {
	while (1) {
		system("sample.exe > 文件名.in");
		system("文件名.exe < 文件名.in > 文件名.out");
		system("std.exe < 文件名.in > 文件名.ans");
		if (system("fc 文件名.out 文件名.ans")) {
			std::cerr << ++cnt << " WA\n";
			break;
		}
		std::cerr << ++cnt << " AC\n";
	}
	return 0;
}

其中sample.exe为造样例程序。

当考场不让使用cmd的时候,略微修改也可以直接对拍已给出样例。

#include 

int main() {
	system("fc 文件名.out 文件名.ans"); 
	return 0;
}

4 特殊实现

对于某一些特殊的题目,我们也可以有一些非常规对拍方法。

4.1 正难则反

若某些题目背景为从答案出发的题目,我们能可以采用这种方法。
对于这种方法,我们需要改变 s t d std std的定义。
新定义:

  • 读入ans,判断ans的正确性。

这种方法难点仅此一点。
补充:对于绝大部分的 S P J SPJ SPJ,对拍程序大多采用该方法

5 举例说明

本蒟蒻比较懒,但是同机房的大佬催着蒟蒻给一个示范,于是就有了这一栏。
本蒟蒻是真的蒟蒻,就写一个我最近打的氵题罢。
洛谷P7909分糖果
(话说这个是不是真的太水了)
先贴需拍代码。

#include 
#define int long long

int n, l, r;

signed main() {
	scanf("%lld%lld%lld", &n, &l, &r);
	if (r > l + n) {
		printf("%lld\n", n - 1);
		return 0;
	}
	if (r / n == l / n) {
		printf("%lld\n", r % n);
		return 0;
	}
	printf("%lld\n", n - 1);
	return 0;
}

显然这是一个AC代码。
(趁Dev-C++不注意改一下。

#include 
#define int long long

int n, l, r;

signed main() {
	scanf("%lld%lld%lld", &n, &l, &r);
	if (r > l + n) {
		printf("%lld\n", n - 1);
		return 0;
	}
	if (r / n == l / n + 1) {
		printf("%lld\n", r % n);
		return 0;
	}
	printf("%lld\n", n - 1);
	return 0;
}

这样他就变成了一个 20 p t s 20pts 20ptsWA代码。
接下来有一个很现实的问题。
暴力怎么写才能确保正确性。
对于这道题来说,只需 O ( r − l ) O(r-l) O(rl)枚举所有可能性即可。
但是,对于其他算法题呢?
显然,搜索与枚举是一种很好的检测方式。
那么,

你可能感兴趣的:(应试小技巧,c++)