先聲明,本文章轉載至:http://blog.csdn.net/wlx65003/article/details/51149196
搞程序設計競賽的同學很多時候都會因為WA但苦苦找不到錯誤數據而苦惱,雖然肉眼debug的能力也很重要,但有的時候一直手打數據測試兩三天也沒有必要。這里就介紹一種對拍程序的寫法,是我改進過的,自認為效率應該是比較高了。
如果你懶得學實現細節了,想直接使用,那么下面的內容可以略過了,到這去下載打包好的,里面有使用教程:
http://pan.baidu.com/s/1boERyGZ
實現細節
首先對拍程序,顧名思義,一個輸入給兩個程序分別跑一遍,看看對不對的上。
那么牽扯到三個步驟:
- 生成一組輸入數據
- 把這組數據分別給兩個程序運行,并生成兩組輸出數據
- 比較兩組輸出數據
看到這個步驟很多人應該已經有想法了,沒錯用文件操作能實現,但太麻煩,因為你得修改你自己的代碼把輸出重定向到一個文件,這要是一不小心忘記刪重定向直接交了又得WA一遍233(不對,是RE),這里介紹一種更方便更高逼格的批處理命令。
首先新建一個批處理文件,命名為簡易對拍程序.bat
,什么你不知道怎么新建?右鍵新建一個文本文檔直接把后綴名改成bat就好啦,因為批處理文件本質上就是一堆命令文本嘛。
然后右鍵—編輯,開始打代碼:
首先第一步:生成一組輸入數據。
我們假設你已經寫好了一個數據生成器,編譯成rand.exe并放在當前目錄下了,那么我們只要把這個程序的輸入重定向到一個文件就行了,如果你直接在源碼里操作,還得各種文件流重定向煩得要死。在批處理命令里很簡單,就一句話:
rand.exe > in.txt
是不是很簡單明了?
那怎么把文件輸入到一個程序里去呢?沒錯:
my.exe < in.txt
std.exe < in.txt
my.exe是你寫的錯誤程序,std.exe是標程
那怎么把兩個程序的輸出再重定向到文件里去呢?也很簡單:
my.exe < in.txt > myout.txt
std.exe < in.txt > stdout.txt
是不是相當方便?
接下來就是比較myout.txt和stdout.txt了,也不用你手寫判斷程序,windows自帶一個比較命令:fc(file compare)
fc myout.txt stdout.txt
如果兩個沒有差異,會顯示:找不到差異,否則會顯示不同的附近的幾行的文本。
匯總一下:
rand.exe > in.txt
my.exe < in.txt > myout.txt
std.exe < in.txt > stdout.txt
fc myout.txt stdout.txt
好,這樣一個簡易版對拍程序就寫好了。但這個功能也太簡陋了,只能對拍一次,要是數據難找點豈不是要你運行到手酸?
有人就問了,能不能循環?答案是:可以!
@echo off
:loop
rand.exe > in.txt
my.exe < in.txt > myout.txt
std.exe < in.txt > stdout.txt
fc myout.txt stdout.txt
if not errorlevel 1 goto loop
pause
goto loop
別懵逼,一行行給你解釋。
首先@echo off
是關掉輸入顯示,不然你的所有命令都會顯示出來的,防止刷屏。
:loop
是定位標記點,和c語言里的goto
很像。
中間是主體程序。
if not errorlevel 1 goto loop
,errorlevel
是上一個命令的返回值,fc
在文件不同時返回1,相同時返回0,這一行的意思就是,如果fc返回的不是1,就跳到:loop
,使勁循環。
pause
,暫停,一旦fc
返回1,就會執行到這一行,停住程序,給你時間看數據。
goto loop
,看完數據,按下任意鍵結束暫停,繼續循環。
這樣一來功能就頓時強大起來了,為了紀念這么偉大的改進,我們把文件名重命名為普通版對拍程序.bat
。(網上流傳的也大多就這個版本了)
但這還不夠! 為什么? 我們看一下rand程序的寫法。
例如題目格式是,T組數據,每組數據一個n,一個m,然后n個1~m的整數
你就這么寫:
#include<bits/stdc++.h>
using namespace std;
#define random(a,b) ((a)+rand()%((b)-(a)+1))
int main( int argc, char *argv[] )
{
int seed=time(NULL);
srand(seed);
printf("1\n");
int n=10;
int m=random(1,20);
printf("%d %d\n",n,m);
for(int i=0 ; i<n ; ++i)
{
printf(" %d ",random(0,m));
}
printf("\n");
return 0;
}
這樣的話有個缺點,time(NULL)
是一秒才更新一次的,也就是說我們的隨機數據一秒才換一次,太慢了!
有沒有什么變的更快的隨機數種子?有!windows自帶了一個隨機數發生器:%random%,它的值就是一個隨機整數,可以在命令行里調用。
那接下來就好辦了,我們把這個數傳給rand.exe用來當隨機數種子就行了。
什么?你不知道怎么傳?
呃,你知不知道main
函數里這兩個參數干嘛用的:int argc, char *argv[]
?
恐怕好多人還不知道,我這里解釋下,這兩個就是傳入參數,argc
是參數個數,*argv[]
是參數表,從1開始。
知道了這個就好辦了。
@echo off
:loop
rand.exe %random% > data.in
std.exe < data.in > std.out
my.exe < data.in > my.out
fc my.out std.out
if not errorlevel 1 goto loop
pause
goto loop
我們把%random%
當參數傳給rand.exe就行了。
然后程序里這么寫:
#include<bits/stdc++.h>
using namespace std;
#define random(a,b) ((a)+rand()%((b)-(a)+1))
stringstream ss;
int main( int argc, char *argv[] )
{
int seed=time(NULL);
if(argc)//如果有參數
{
ss.clear();
ss<<argv[1];
ss>>seed;//把參數轉換成整數賦值給seed
}
srand(seed);
//以上為隨機數初始化,請勿修改
//random(a,b)生成[a,b]的隨機整數
//以下寫你自己的數據生成代碼
printf("1\n");
int n=10;
int m=random(1,20);
printf("%d %d\n",n,m);
for(int i=0 ; i<n ; ++i)
{
printf(" %d ",random(0,m));
}
printf("\n");
return 0;
}
我這里用stringstream
把字符串轉換成整數,你們也可以用其他辦法。
這么一來,數據測試效率就得到了上千倍的提升!為了紀念這么偉大的改進,我們把程序重命名為狂拽酷炫吊炸天對拍程序.bat
。
這么一來對拍程序就徹底完成啦!完結撒花!
你只需把my.cpp和std.cpp放在和對拍程序相同的目錄下
my.cpp里放你自己的代碼,編譯成my.exe
std.cpp里放標程,編譯成std.exe
(推薦用dev-c++,單文件編譯方便)
然后雙擊運行對拍程序,等待它暫停,然后打開data.in就能看到對拍出來的數據啦~