【杂文】搞一个对拍程序
【杂文】搞一个对拍程序
有时候可能我们需要数据来对程序进行差错,但良心的出题人又只给了你一个连蒙都能蒙对的数据。
对拍程序,不花时间不考码力不费脑,\(OIer\) 居家旅行必备佳品。
【原理】
采用 <\(cstdlib\)> 里的函数。
简单来说就是无限循环着做三件事:
-
【生成数据】
可以写一些较简单的数据方便分析并改进代码,也可以写一些较庞大的数据来测试代码运行时间。 -
【跑一遍标程】
一般是写一个暴力程序作为标程(什么?你说你不会写暴力?\(QAQ\)?),除了一些毒瘤,大部分题都是可以直接暴力枚举的。
如果只是在平时刷题的话,也可以直接嫖一篇题解里的代码。 -
【跑一遍我们自己写得程序】
-
【文本差异对比】
使用指令 \(fc\) 进行对比,如果不相同则输出 \(FBI\) \(WARNING!!!\)(逃)
【生成数据代码写法】
<\(cstdlib\)> 中有一个函数 \(rand()\),它可以将某一个初始值代入并进行复杂的运算后生成一个与其看似毫不相关的数,然后再改变这个初始值生成下一个数据,用这个可以生成大量的随机数据。但不管怎样,这个最初的初始值是不会变的,计算方式也总是相同的,所以呢,如果重新运行的话,将会生成与上一次一模一样的数据(可以自己尝试一下)。于是就需要用到 \(srand((unsigned)time(0))\) 控制它的代入值,它调用了函数库 <\(ctime\)> 里的函数 \(time()\) 作为代入值,可以保证每次生成的数据都不同。
但对于一些非常规题目生成数据写起来可能会比较痛苦,如果遇到了可以看一下这个项目: \(CYaRon\) 。
\(Luogu\) 帖子
\(Github\) 项目
【Code】
【对拍 .cpp】
注意:如果要用这个代码的话,记得把 \(freopen()\) 注释掉再运行,因为这里已经有调用文件了。
#pragma GCC optimize(3,"Ofast","inline")
#pragma GCC optimize(2)
#include<iostream>
#include<windows.h>
#define Re register int
int main(){
Re sec=0.1;//生成数据间隔时间,这里是每 0.1秒搞一次
sec*=1e3;
while(1){
system("data.exe>data.in");//生成数据扔进文件
system("std.exe<data.in>std.out");//生成数据后跑一遍std标程
system("myprogram.exe<data.in>my.out");//用力把数据插进去-->我自己写出的全WA却能过样例的程序
if(system("fc std.out my.out")){//比较标志指令 fc,如果文本不一样则返回1
system("pause");
printf("FBI WARNING!!! 吃柠檬吧数据卡掉了我\n");
//break;
}
Sleep(sec);
}
}
【data.cpp】
#pragma GCC optimize(3,"Ofast","inline")
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#include<windows.h>
using namespace std;
int main(){
srand((unsigned)time(0));//调用时间,保证每次rand()出来的数据都不同)
}
【std.cpp】
#pragma GCC optimize(3,"Ofast","inline")
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
int main(){
printf("time:%lf\n",(double)clock()/(double)CLOCKS_PER_SEC);//输出程序运行时间
}
【myprogram.cpp】
#pragma GCC optimize(3,"Ofast","inline")
#pragma GCC optimize(2)
#include<algorithm>
#include<cstdio>
#include<ctime>
int main(){
printf("time:%lf\n",(double)clock()/(double)CLOCKS_PER_SEC);//输出程序运行时间
}
【数据生成示例】
老师曾组织过的一次考试用的是 \(2008\) 年的提高组原题,闲得无聊时给 \(T2\) \(,\) \(T3\) \(,\) \(T4\) 写了一共三个对拍(不要问我为什么会没有 \(T1\), 你自己去看看 \(T1\) 是个什么鬼玩意儿题就知道了)。没想到还真用上了,\(T4\) 发现了一组 \(Hack\) 数据,改进代码后 \(T4\) 才拿的满分。
后来发现网上大部分题解都是错的,包括 \(Luogu\),一大堆 \(AC\) 的错误的代码,其实 \(Luogu\) 还好,只是没有放特殊数据,而某 \(Hunter\) 我就无语了,直接丢一堆错误数据,也不知道哪儿能提供更改意见。。。。。。。
\(T4\) 的 \(Hack\) 数据:
输入:
8
2 3 1 5 6 4 7 8
输出:
a c a b b a d c a b b a d b a b
好了,不废话了,上题上代码。
\(T2:\) 火柴棒等式 \([P1149]\)
** 【\(data.cpp\)】**
这个简单到爆哎
#include<windows.h>
#include<cstdio>
#include<ctime>
using namespace std;
int main(){
srand((int)time(NULL));
int n=rand()%25;
printf("%d\n",n);
}
【\(std.cpp\)】
标程可看可不看
#include<cstdio>
int g[10]={6,2,5,5,4,5,6,3,7,6};
int i,j,n,x,ans,s[2500];
int main(){
scanf("%d",&n);n-=4;
s[0]=6;
for(i=0;i<=2450;i++,x=i)while(x)s[i]+=g[x%10],x/=10;
for(i=0;i<=1200;i++)
for(j=0;j<=1200;j++)
if(s[i]+s[j]+s[i+j]==n)ans++;
printf("%d\n",ans);
}
\(T3:\) 传纸条 \([P1006]\)
【\(data.cpp\)】
#include<windows.h>
#include<cstdio>
#include<ctime>
using namespace std;
int i,j;
int main(){
srand((int)time(NULL));
int n=rand()%(10-5)+5,m=rand()%(50-5)+5;
printf("%d %d\n",n,m);
for(i=1;i<=n;printf("\n"),i++)
for(j=1;j<=m;j++){
if((i==1&&j==1)||(i==n&&j==m)){printf("0 ");continue;}
printf("%d ",rand()%101);
}
}
【\(std.cpp\)】
#include<algorithm>
#include<cstring>
#include<cstdio>
using std::min;using std::max;
int n,m,g,t,i,j,k,a[15][55],dp[120][55][55];
int main(){
memset(dp,-1,sizeof(dp));
scanf("%d%d",&n,&m);g=m+n-1;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
scanf("%d",&a[i][j]);
dp[1][1][1]=a[1][1];
for(k=1;k<=g;k++){
t=min(n,k);
for(i=1;i<=t;i++)
for(j=1;j<=t;j++)
if(i!=j)dp[k][i][j]=a[i][k-i]+a[j][k-j]+max(max(dp[k-1][i][j],dp[k-1][i-1][j]),max(dp[k-1][i][j-1],dp[k-1][i-1][j-1]));
}
printf("%d",max(dp[g][n][n-1],dp[g][n-1][n]));
}
\(T4:\) 双栈排序 \([P1155]\)
【\(data.cpp\)】
#include<windows.h>
#include<cstdio>
#include<ctime>
using namespace std;
int i,pan[1005];
int main(){
srand((int)time(NULL));
int n=rand()%(10-5)+5;
// int n=100;
printf("%d\n",n);
for(i=1;i+2<=n;){
printf("%d %d %d ",i+1,i+2,i);
if(i+3<=n)i+=3;
else break;
}
for(;i<=n;i++)printf("%d ",i);
}
【\(std.cpp\)】
#include<algorithm>
#include<cstdio>
using std::min;
int i,j,n,x,y,t,t1,t2,cnt,a[1005],f[2010],Q1[1005],Q2[1005],last[1005],zhan[1005];char ans[2010];
int find(int x){if(f[x]!=x)f[x]=find(f[x]);return f[x];}
// 首次接触并查集扩展域,翻书学了。。。。
// x 有两层含义,一是编号,一是状态
// x<n 表示 编号x 在栈 1 的状态
// x>n 表示 编号 (x-n) 在栈 2 的状态
// f[] 把未知个各种各样的状态连成了一块,并且这一块内的状态必须同时存在
// f[x]=y 表示 在以 y 作为头的这个分块里面,必须存在着 x 这一种状态
inline void judge(){
for(i=1;i<=2*n;i++)f[i]=i;last[n]=1e9;
for(i=n-1;i>=1;i--)last[i]=min(a[i+1],last[i+1]);
for(i=1;i<n;i++)
for(j=i+1;j<n;j++)
if(a[i]<a[j]&&a[i]>last[j]){
x=find(i),y=find(j);
if(x==y){printf("0\n");exit(0);}
f[x]=find(j+n),f[y]=find(i+n);
}
}
inline void pop1(){
while(t1&&Q1[t1]==cnt)ans[++t]='b',t1--,cnt++;
}
inline void pop2(){
while(t2&&Q2[t2]==cnt)ans[++t]='d',t2--,cnt++;
}
int main(){
scanf("%d",&n);
for(i=1;i<=n;i++)scanf("%d",&a[i]);
judge();
for(i=1;i<=n;i++)
if(zhan[i]!=2){
zhan[i]=1;
for(j=i+1;j<=n;j++)
if(find(i)==find(j+n))
zhan[j]=2;
}
cnt=1;t1=t2=0;
for(i=1;i<=n;i++)
if(zhan[i]==1){
pop1();
ans[++t]='a',Q1[++t1]=a[i];
pop2();
}
else{
pop1();
pop2();
ans[++t]='c',Q2[++t2]=a[i];
}
while(cnt<=n)pop1(),pop2();
for(i=1;i<t;i++)printf("%c ",ans[i]);printf("%c",ans[t]);
}