前言
写数据生成器是实现对拍必不可少的环节,只有写出正确的数据生成器让对拍器正常工作。
关于对拍器的实现:对拍器
一.随机数生成
我们知道,用rand()可以产生之间的伪随机数,而且在使用rand()之前,我们往往要使用:
srand(time(0));
来初始化随机数种子。
其中 RAND_MAX 往往是short的最大值,为32767(一般在Windows系统下),有些情况下这个数还不够大,所以我们需要将其改为在int范围内的随机数
我们采用宏定义的方法来对rand()进行扩展:
#define DEV_RND ((int)rand()*RAND_MAX+rand())
#define RND(L,R) (DEV_RND%((R)-(L)+1)+(L))
其中 DEV_RND 用于返回一个 int 范围内的伪随机数,RND(L,R) 用于返回一个在 范围内的伪随机数
二.简单数据生成
对于很多题目我们直接输出若干个随机数即可制成一组数据,直接用for循环控制即可。
例题讲解:
例如:小凯的数字
这道题是我的模拟赛的第一题,数据比较好生成,当时我是用VB随机数生成器制作的所有数据。
现在我们用C++来写这道题的数据生成器:
#include<bits/stdc++.h>
using namespace std;
#define DEV_RND ((int)rand()*RAND_MAX+rand())
#define RND(L,R) (DEV_RND%((R)-(L)+1)+(L))
int main(){
int T;
srand(time(0));
T=RND(1,1000);
printf("%d\n",T);
while(T--){
int a=RND(1,1000000),b=RND(1,1000000);
printf("%d %d\n",min(a,b),max(a,b));
}
return 0;
}
上面便是一个随机生成前70%数据的随机数生成器,至于生成更大的数,请读者自行实现
对于上面这道题的数据生成方法我们可以进行推广,从而制作解决大部分数据的数据生成器
三.数据结构的数据生成器
1):树的生成
模板:
#include<bits/stdc++.h>
using namespace std;
#define DEV_RND ((int)rand()*RAND_MAX+rand())
#define RND(L,R) (DEV_RND%((R)-(L)+1)+(L))
int n;//节点个数
pair<int,int>edge[100005];
int main(){
srand(time(0));
scanf("%d",&n);
printf("%d\n",n);
for(int i=2;i<=n;i++){
int fa=RND(1,i-1);
edge[i-1]=make_pair(i,fa);
}
random_shuffle(edge+1,edge+n);//随机打乱
for(int i=1;i<n;i++)
printf("%d %d\n",edge[i].first,edge[i].second);
return 0;
}
模板很容易理解
2):图的生成
模板(不含重边):
#include<bits/stdc++.h>
using namespace std;
#define DEV_RND ((int)rand()*RAND_MAX+rand())
#define RND(L,R) (DEV_RND%((R)-(L)+1)+(L))
int n,m;//节点个数,边数
pair<int,int>edge[100005];
map<pair<int,int>,bool>mp;
int main(){
srand(time(0));
scanf("%d%d",&n,&m);
printf("%d %d\n",n,m);
for(int i=2;i<=n;i++){
int fa=RND(1,i-1);
edge[i-1]=make_pair(i,fa);
mp[edge[i-1]]=true;
}
for(int i=n;i<=m;i++) while(true){
int a=RND(1,n),b=RND(1,n);
if(a<b) swap(a,b);
if(a==b||mp.count(make_pair(a,b))) continue;
edge[i]=make_pair(a,b);
mp[edge[i]]=true;
break;
}
random_shuffle(edge+1,edge+m+1);//随机打乱
for(int i=1;i<=m;i++)
printf("%d %d\n",edge[i].first,edge[i].second);
return 0;
}
也比较好懂,注意:
1.输入要满足m>=n-1
2.如果边数超过n*(n-1)/2将会死循环,如果接近n*(n-1)/2运行时间将可能很久
3):例题讲解
Sample1:PION后缀自动机
这是我出的模拟赛的压轴题,数据生成器需要构造一颗树以及生成一些询问
#include<bits/stdc++.h>
using namespace std;
#define DEV_RND ((int)rand()*RAND_MAX+rand())
#define RND(L,R) (DEV_RND%((R)-(L)+1)+(L))
const int maxn=1000005;
struct edge{//储存树边
int to,next;
}e[maxn*2];
int head[maxn],np;
void adde(int u,int v){//添加树边
e[++np]=(edge){v,head[u]};
head[u]=np;
e[++np]=(edge){u,head[v]};
head[v]=np;
}
pair<int,int>edg[maxn];
int n,m,files,cnt,k[maxn],mp[maxn];
void print_str(int v){
while(v){
putchar(v%26+'a'),v/=27;
}
}
int main(){
freopen("in.in","w",stdout);
srand(time(0));
n=RND(18000,20000),m=RND(18000,20000),cnt=RND(400000,500000);
for(int i=2;i<=n;i++){
edg[i]=make_pair(RND(1,i-1),i);
if(files<cnt) k[i]=RND(0,10),files+=k[i];
}
printf("%d %d\n",n,m);
random_shuffle(edg+2,edg+n+1);
for(int i=2;i<=n;i++){
printf("%d %d\n",edg[i].first,edg[i].second);
adde(edg[i].first,edg[i].second);
}
int a,b;
for(int i=1;i<=n;i++) mp[i]=RND(1,30000);
for(int i=1;i<=n;i++){
printf("%d",k[i]);
while(k[i]--) putchar(' '),print_str(mp[RND(1,n)]);
putchar('\n');
}
for(int i=1;i<=m;i++){
int id=RND(1,3);
if(id==1){
printf("query /p %d %d\n",RND(1,n),RND(1,n));
}
else if(id==2){
printf("query /e %d %d *.",RND(1,n),RND(1,n)),print_str(mp[RND(1,n)]);
putchar('\n');
}
else{
printf("del /e %d %d *.",RND(1,n),RND(1,n)),print_str(mp[RND(1,n)]);
putchar('\n');
}
}
return 0;
}
这是我写的数据生成器中的一个,代码还是比较简单吧,估计在考场上需要10~15分钟完成该数据生成器
Sample2:流量计算
这是还是我出的模拟赛的题目,这可能是我出的题目中数据最难生成的一道题,我为了生成比较完整的数据写了4个不同的数据生成器,下面便是其中最具代表性的一个:
#include<bits/stdc++.h>
using namespace std;
#define DEV_RND ((int)rand()*RAND_MAX+rand())
#define RND(L,R) (DEV_RND%((R)-(L)+1)+(L))
const int maxn=70000;
int id[maxn],n,m,cnt;
int x[maxn],y[maxn],z[maxn];
double R_sum=0,k=1.0,RR[maxn];
int main(){
freopen("in.in","w",stdout);
srand(time(0));
n=RND(20000,20000);
for(int i=1;i<=n;i++) id[i]=i;
random_shuffle(id+1,id+n+1);
x[0]=id[1],y[0]=id[n],z[0]=RND(900000,1000000);
for(int i=1;i<n;i++) x[i]=id[i],y[i]=id[i+1],z[i]=RND(80,100);
m=n-1;
for(int i=1;i<n;){
int nxt=RND(1,5);
if(i+nxt>n){
R_sum+=(double)z[i];i++;continue;
}
double R_=0,R_max;
for(int j=i;j<i+nxt;j++) R_+=(double)z[j];
R_max=R_;
int t=RND(2,5);
int R__;
while(t--){
m++;
x[m]=id[i],y[m]=id[i+nxt],z[m]=R__=RND(80,100);
R_=R_*R__/(R_+R__);
R_max=max(R_max,(double)R__);
}
R_sum+=R_;
RR[++cnt]=R_;
k=min(k,R_/R_max);
i+=nxt;
}
printf("%d %d\n",n,m+1);
printf("%d %d P %d\n",x[0],y[0],z[0]);
for(int i=1;i<=m;i++) printf("%d %d R %d\n",x[i],y[i],z[i]);
// freopen("ans.out","w",stdout);
// for(int i=cnt;i;i--) printf("%.2lf\n",RR[i]);
// printf("%.2lf %.2lf\n",R_sum,k);
// printf("%.2lf\n%.2lf\n",(double)z[0]/R_sum,k*z[0]/R_sum);
// getchar();
return 0;
}
这个数据生成器就不那么好写了,上面的能生成比较多情况的满足题目的数据,值得注意的是我在数据生成时利用题目的特性在生成数据时便可以生成答案,在能保证生成正确数据和答案的情况下,这种方法使数据生成器可以达到数据生成器和暴力比较程序的双重作用。