如何造数据
很多人将造数据看成小儿科,但并不是这样的,往往造数据要思考的甚至比写代码要思考的多了多。
下面让我简单的介绍一下:
基本的随机代码
先来看随机造数的函数。
C++:
//需要用到的库
#include<cstdlib>
#include<windows.h>
srand()//初始化(放在程序开头),如果不初始化,生出来的东西是一样的
srand(time(0))//较慢的初始化,要调ctime库
srand(GetTickCount())//毫秒级初始化(反正就是快),要调windows.h库
rand()//生成一个short int类型的非负整数
//如果要生成int类型的数就得写个函数
int brand(){
return (rand()<<15)+(rand()<<1)+(rand()&1);
}
//这样就好了
pascal:
randomize;//初始化(很快,放在程序开头)
random(x)//生成一个0到x-1的整数
本人只会这两种语言。。。
树形结构的构造
造数据时,树形结构是十分麻烦的,如果你想生成一棵树,如果纯随机的话,经常生出一个环来。所以这时候就要思考了。
1.我们根据树的定义,知道每个节点当且仅有一个父节点,那么我们就为每个节点选一个父节点,然后建图,完美解决。
2.我们学过Kruskal,知道它的原理,就是从最小的开始,看两端点是否连通,不连通就连上。我们可以根据这个方法,先胡乱造一通,劲量多造,然后打乱(其实也可以不打乱,因为本身就是乱的),然后用并查集,将无用的边删去。最后还需要check一趟,因为你不能保证刚好是一个连通块,经常出现多个的情况,所以有意的加入几条边,整体还是随机的。
方法还有很多,或许大佬们还有更好的想法。
一个难造的造数据(题目)
下面给你们一道小题(同学出的),你可以尝试去造,但是,我是造(tai)不(lan)来(le)的。
接电路
(electricity.pas/c/cpp)
【题目背景】
这次,ZYK和WLW正在进行电学实验,ZYK装逼气焰十分嚣张,出了难题考考WLW
【题目描述】
在WLW和ZYK的试验中,一共有N(1≤N≤79)种型号的用电器,每种用电器的个数无限多
ZYK与WLW不屑于简单的并联、串联电路,他们常常会尝试复杂的混联电路!
但是这些用电器质量不过关,每一个用电器都有P的可能断路(0≤P≤1)
假若表示一个简单的混联电路,是这样的:
![]()
ZYK果断嫌太烦,于是WLW改进了表示方法,上面这个电路,可以这样表示:
A,(B)(C)(D)
下面解释一下:
1.一个元件是最小的电路,最多有79种用电器,分别用字符0到字符~(ASCII码从48到126一共79个,放心,这些关键字符号(,)肯定不在那里面)
2.K个电路组成的串联电路表示为电路1,电路2,……,电路K
3.K个电路组成的并联电路表示为(电路1)(电路2)……(电路K)
【任务】
现在ZYK想让WLW求出电源两端断路的概率
【输入格式】
第1行是一个整数N,表示用电器型号总数
第2行是一个表示电路的字符串,如题目描述所示
接下来n行,每行一个字符C(0到~)和一个实数P(小数点后最多6位小数),中间用一个空格隔开,P表示对应用电器C断路的概率,数据保证C各不相同
【输出格式】
一行一个实数N,表示电源两端断路的概率,四舍五入保留6位小数
【样例输入】
5
(A,B)((C)(D),E)
A 0.2
B 0.3
C 0.4
D 0.5
E 0.6
【样例输出】
0.299200
【数据范围】
对于9%的数据:N≤10,且全部为数字,电路字符串长度≤40
对于27%的数据:N≤26,且全部为大写字母,电路字符串长度≤200
对于54%的数据:N≤52,且全部为大小写字母,电路字符串长度≤3000
对于81%的数据:N≤79,电路字符串长度≤80000
对于100%的数据:N≤79,电路字符串长度≤1000000
【注释】
本题不开-O2编译优化
时间限制:1秒
空间限制:64MB
其实这个可以用DFS构造,但是由于注意事项太多,本人懒的码代码。
反例的构造
造反例,有时是很容易的,但是数据一大,你就很难把控,特别是图论的题目,很有可能会绕过你专门造的路线。所以,当遇到这种题目时,多造几组对拍一下。
造反例还需要思考很多东西,因为有些代码是根据题目描述,猜出数据方向而写的。
有些题目,像NOIP2017第三题,数据太弱了,弱到写个从左上角到右下角的DP都能拿95分,想这种DP,随便造一个回路就可以放倒,除非是N次DP,结果,就一个点放倒了,这就是出题人没考虑到,或者太(tai)懒(cai)了(le),懒(cai)到抠脚。
所以说,每当造数据时,一定要思考往哪个方向造数据,尽量不要让人骗分骗到手。
造反例有一个很好有的方法,就是对拍,拍它个一个小时,反例就到手了(但是没有好的代码,可能永远也停不了)。
对拍是用批处理写的
:loop
maker//造数据
a.exe//标程(.exe可以不写)
b.exe//有反例的程序或可能有反例的程序
fc a.out b.out//比较输出文件
if errorlevel==1 pause//如果不同,就停止
goto loop//继续
对拍拍停了,那么反例就出来了。在比赛中很好用,有了新想法,和笨蛋对拍一下,就知道大致是否有问题。然后,跟着数据模拟,三下两下错误就找到了。
如何造出最坏的数据
就举个例子吧,比如像是区间类的题目,just like区间求最大值(其实这个很简单,只是举个例子)。
对于Q次询问L,R(1≤L≤R≤N)。
输出L到R区间的最大值。
这种数据构造很简单:
for(int i=1;i<=Q;i++){
int L=rand()%N,R=rand()%N;
if(R<L) swap(L,R);
printf("%d %d\n",L,R);
}
有人会写O(N^2)的扫描
for(int i=1;i<=Q;i++){
int L=read(),R=read();//读入
ans=0;
for(int j=L;j<=R;j++) ans=max(ans,a[j]);
printf("%d\n",ans);
}
然而,(理论估计)当数据N,Q上到10^4时就不行了。但是,你会发现,这种笨蛋照样能杀满。
因为询问的边界L和R是随机给的,区间跨度有可能很小,时间也就上不到O(N*Q)。
所以当这种情况,我们需要限制一个边界的跨度,比如5*10^3,因为如果都是1到N的话很容易被人猜中。
for(int i=1;i<=Q;i++){
int L=rand()%N,R=rand()%N;
if(R<L) swap(L,R);
while(R-L<5000){
L=rand()%N,R=rand()%N
if(R<L) swap(L,R);
}
printf("%d %d\n",L,R);
}
只是举个例子,像这种情况还有很多,题目也不少,这里就不一一介绍了。