對拍
注; NK版權所有,轉載請表明出處
定義:
什么是對拍? 當我們的程序過了樣例,是否意味着它一定能AC呢?顯然大多數情況下都是不行的。所以我們需要自己設計一些數據來測試我們的程序,但有的題目數據很大,我們肉眼無法看出程序計算的結果是否正確,手工計算又非常耗時,在緊張的比賽中,我們該怎么應對呢?於是有了對拍。 對拍簡單的說就是當你寫完一個題目的程序以后,再寫一個暴力求解該題目的程序,然后自己生成一些測試數據,看同樣的數據,兩個程序輸出的結果是否相同,不同意味着被對拍的程序有問題。以此來幫助你修改程序,提高通過率的方法,我們稱為對拍。
對拍的例子1
【題目描述】
輸入n個整數,現在有m個形如[x,y]的提問,即問第x個數到第y個數之和是多少?現在需要你寫一程序對每個提問做出快速回答。 1<=n<=100000 1<=m<=50000
輸入格式: 第一行,兩個整數n和m 第二行,n個空格間隔的整數,每個整數的范圍在[-10000,10000]之間 接下來m行,每行兩個整數x和y,表示一次詢問的區間(x<=y)
輸出格式: m行,每行一個整數,對應一次提問的答案
輸入樣例: 5 3 1 3 2 7 9 1 2 2 5 3 4
輸出樣例: 4 21 9
第一步:此題顯然是前綴和,於是寫代表正解的程序(ZhengJie.cpp)
1 int Sum[100005]; 2 int main() 3 { 4 freopen("data.in","r",stdin); //從文件data.in中讀入數據 5 freopen("ZhengJie.out","w",stdout); //輸出的結果存在ZhengJie.out文件中 6 int i,n,m,tmp,x,y; 7 scanf("%d%d",&n,&m); 8 for(i=1;i<=n;i++) 9 { 10 scanf("%d",&tmp); 11 Sum[i]=Sum[i-1]+tmp; 12 } 13 for(i=1;i<=m;i++) 14 { 15 scanf("%d%d",&x,&y); 16 printf("%d\n",Sum[y]-Sum[x-1]); 17 } 18 } 19 20 /* 21 我們把這個程序保存為ZhengJie.cpp,但這個程序是否正確呢? 22 我們再寫一個暴力程序來驗證它 23 */
第二步:寫一個暴力程序,這里我寫的是暴力枚舉(BaoLi.cpp)
1 int a[100005]; 2 int main() 3 { 4 freopen("data.in","r",stdin); //注意,暴力程序讀入的數據仍然是data.in 5 freopen("BaoLi.out","w",stdout); //暴力程序輸出的結果是BaoLi.out 6 int n,m,i,j,x,y,ans; 7 scanf("%d%d",&n,&m); 8 for(i=1;i<=n;i++)scanf("%d",&a[i]); 9 for(i=1;i<=m;i++) 10 { 11 scanf("%d%d",&x,&y); 12 ans=0; 13 for(j=x;j<=y;j++)ans+=a[j]; 14 printf("%d\n",ans); 15 } 16 } 17 我們把這個程序保存為BaoLi.cpp 注意:我們不在乎暴力程序效率,只需要保證它的結果是正確的就行了。
第3步:我們還需要一個生成隨機數據的程序(MakeData.cpp)
1 //該程序按照題目給定的格式生成隨機數據。 2 #include<cstdlib> //加入這個包才能使用隨機函數rand() 3 #include<cstdio> 4 #include<ctime> //加入這個包就能以時間為種子初始化隨機函數 5 #include<iostream> 6 using namespace std; 7 int main() 8 { 9 freopen("data.in","w",stdout); //注意:該程序生成的數據到data.in中 10 srand(time(NULL)); //重要:初始化隨機函數,以時間為種子 11 int n=rand()%10000+1; //生成一個1到10000之間的隨機整數n 12 int m=rand()%10000+1; 13 printf("%d %d\n",n,m); 14 for(int i=1;i<=n;i++)printf("%d ",rand()%20000-rand()%10000);//生成-10000到10000間的數字 15 printf("\n"); 16 for(int i=1;i<=m;i++) 17 { 18 int x=rand()%n+1; //保證生成的數據是x<=y 19 int y=x+rand()%n+1; 20 if(y>n)y=n; 21 printf("%d %d\n",x,y); 22 } 23 } 24 注意: 25 rand()只能生成0到32767之間的隨機整數,如果要生成1到50000之間的整數,可以寫成: 26 rand()%30000+rand()%20000+1
第4步:寫windows對拍文件(對拍.bat)




注意:今天學的是在Windows下的對拍! 全國比賽是在linux系統里寫程序,linux下的對拍我們以后再談!
這里提供兩種比較麻煩的數據生成模板
樹
#include <cstdio> #include <cstdlib> #include <ctime> using namespace std; int main() { //節點數量 int n=100000; //文件名 char file_name[100]="datax.in"; srand(time(0)); FILE* f=fopen(file_name,"w"); fprintf(f,"%d\n",n); for(int i=2;i<=n;i++) { int j=i-1-rand()%7; j=j<1?1:j; fprintf(f,"%d %d\n",j,i); } return 0; }
比如說這道題的數據程序(自己感覺比較典型)

修改程序如下
#include <cstdio> #include <cstdlib> #include <ctime> #include<stdio.h> #include<bits/stdc++.h> using namespace std; int main() { srand(time(0));//在第一行保證n是隨機的 int n=rand()%2000; char file_name[100]="1.in";//我是輸出在1.in里面的 FILE* f=fopen(file_name,"w"); fprintf(f,"%d ",n);//輸出n int k=rand()%2000; fprintf(f,"%d\n",n);//輸出k for(int i=1;i<=n;i++) { int u=rand()%2000; fprintf(f,"%d ",u);//輸出a[i] } cout<<endl; for(int i=2;i<=n;i++) { int j=i-1-rand()%7; j=j<1?1:j; fprintf(f,"%d %d\n",j,i);//樹的輸出 } return 0; }
看看結果


數據是有大有小的

在對拍之前還要注意要先編譯所有cpp,保證在同一文件夾下有exe文件
然后就可以愉快的對拍了
圖
#include <cstdio> #include <cstdlib> #include <ctime> #include <set> using namespace std; int main() { //節點數量 int n=100000; //邊數量 int m=500000; //文件名 char file_name[100]="datax.in"; srand(time(0)); FILE* f=fopen(file_name,"w"); fprintf(f,"%d %d\n",n,m); static int vertex[100000+5]; for(int i=2;i<=n;i++) { int j=(rand()<<15|rand())%i+1; vertex[i]=vertex[j]; vertex[j]=i; } set<pair<int,int> > edge; for(int i=1;i<=m;i++) { int u,v; while(true) { u=(rand()<<15|rand())%n+1; v=u+1+rand()%10; if(v<=n&&edge.find(make_pair(u,v))==edge.end()) break; } fprintf(f,"%d %d\n",vertex[u],vertex[v]); edge.insert(make_pair(u,v)); } return 0; }
