不少人考noip之類的比賽永遠會發生一些奇怪的問題ios
好比說下面這兩位(來自個人兩位學長)c++
sliver n:spli,考得怎樣啊?算法
spli:就那樣啦,day1T1沒推出來規律,別的還好windows
silver n:看來你省一高分預約啊函數
幾周後。。。工具
silver n:分出來了嗎?ui
spli:出來了。。。spa
silver n:怎麼了?命令行
spli:別提了,day2T2掛了,沒拿上省一3d
silver n:嗯???你寫的不是正解麼?
spli:是啊,但是我邊界處理出鍋了。。
silver n:。。。
好吧,以上是一個本能進隊的大佬無奈退役的經歷
同時也告訴了咱們檢查的重要性
廢話少說,進入正題
不少時候咱們在考場上老是會手殘的犯一些錯誤(好比邊界什麼,大小寫,變量名)
而後你眼殘也看不出來,怎麼辦呢?
咱們今天就要講這些的剋星:對拍
對拍不能解決一切問題,但卻能夠解決你解決不了的問題
工具:一臺電腦(沒錯,只用一臺電腦)
軟件:命令提示符(別告訴我你家電腦上沒有這個東西),記事本,c++編譯器
下面咱們來開始愉快的對拍之旅(這裏講的是關於命令提示符的對拍,你們能夠把他搬到c++裏(用windows庫),但容易出鍋(我就出過幾回))
相信你們通常用隨機數都是用
#include<cstdlib>
庫中的rand()函數,但這顯然是不正確的
由於rand()是僞隨機數!!!
只要出題人想卡你,跑兩遍隨機數,打個表,數據避開一下
你的答案一不當心就出鍋了
因此,今天咱們學如何生成真正的隨機數:
首先,咱們先明確一點,c++裏面的隨機數是一種算法
這個算法依賴於一個被稱爲種子的數據
種子通常狀況下是1
因爲算法是固定的,因此種子不一樣,隨機數也就不一樣
因此關鍵就在於隨機數種子的生成。
咱們通常使用時間種子生成器(time()函數)
(若是以時間爲種子,這個種子一秒鐘一變,出題人想卡你的話那他簡直是瘋了)
怎麼用呢?看下面:
首先咱們要添加time()的頭文件:
#include<ctime>
以後,咱們就可使用這個函數了!!看圖
有了種子,咱們就要利用種子生成隨機數了。
咱們有請srand()函數登場。
srand()函數相似於cmp或者重載運算符函數之類的函數
在這裏他被用於改變種子
用法:
先加頭文件(和rand()同樣):
#include<cstdlib>
而後看下面:
其實還有一點,是文件操做,我就默認大家會了(每次考試都會用)
好了,該有的都有了,下面咱們就來一個製造數據的實例吧!
我以洛谷P3372【模板】線段樹 1爲例:
如題,已知一個數列,你須要進行下面兩種操做:
1.將某區間每個數加上x
2.求出某區間每個數的和
輸入格式:
第一行包含兩個整數N、M,分別表示該數列數字的個數和操做的總個數。
第二行包含N個用空格分隔的整數,其中第i個數字表示數列第i項的初始值。
接下來M行每行包含3或4個整數,表示一個操做,具體以下:
操做1: 格式:1 x y k 含義:將區間[x,y]內每一個數加上k
操做2: 格式:2 x y 含義:輸出區間[x,y]內每一個數的和
輸出格式:
輸出包含若干行整數,即爲全部操做2的結果。
數據規模:
對於30%的數據:N<=8,M<=10
對於70%的數據:N<=1000,M<=10000
對於100%的數據:N<=100000,M<=100000
(數據已通過增強^_^,保證在int64/long long數據範圍內)
由題目可知,咱們要造的是n,m,n個原始節點,m次操做,且節點值的和在long long範圍內,n,m<=100000
#include<iostream> #include<cstdio> #include<ctime> #include<cstdlib> #define rii register int i #define p 100000 using namespace std; long long seed; long long n,m; int main() { freopen("xds1.in","w",stdout);//文件操做,獲得輸入文件 seed=time(0); srand(seed); n=rand();//windows下rand()max爲32768,爲了有必定的強度,咱們乘一下 n*=n;//n,m這裏你也能夠手動取值 n%=p; m=rand(); m*=m; m%=p; // n=10,m=10; printf("%lld %lld\n",n,m); for(rii=1;i<=n;i++)//製造原始大小 { long long out=rand(); out*=2333; long long fh=rand();//添加負數 if(fh%2==0) { fh=1; } else { fh=-1; } printf("%lld ",out*fh); } printf("\n"); for(rii=1;i<=m;i++)//製造操做 { long long cz=rand(); if(cz%2==1)//生成修改操做的數據 { printf("1 ");//生成添加操做的數據 long long fh=rand();//添加負數 if(fh%2==0) { fh=1; } else { fh=-1; } long long l=rand(),r=rand(),val=rand(); l*=l; l%=n; if(l==0) { l=1; } r*=r; r%=n; if(r==0) { r=1; } val*=fh; val*=2333; if(l>r) { swap(l,r); } printf("%lld %lld %lld\n",l,r,val); } else //生成查詢操做的數據 { printf("2 "); long long l=rand(),r=rand(); l*=l; l%=n; if(l==0) { l=1; } r*=r; r%=n; if(r==0) { r=1; } if(l>r) { swap(l,r); } printf("%lld %lld\n",l,r); } } //區間和最大值上限在我寫的數據中爲 //32768*100000*100000*2333=764477440000000000 // long long max=2^63-1=9223372036854775807,確保符合題意 }
下面是一組我造出來的數據(爲了能放的下,n,m我手動設定爲10)
10 10
1632165 90637 4875292 6168675 -6979981 3452128 5007170 -6946895 3268524 1131914
1 4 9 6775873
1 4 9 -1544790
1 6 9 6904023
2 5 6
1 1 4 3328405
1 1 6 2169929
2 4 9
1 1 9 3784852
1 1 6 -1164534
1 1 4 -5979013
怎麼樣?還不錯吧?
3.windows文件操做(windows script語言)
這個你們可能不太熟悉,
不過不要緊,我會把可能用到的都講一遍。
也就是比較兩個文件,判斷這兩個文件是否相等
咱們通常用命令提示符(cmd.exe)操做(也能夠寫成.bat批處理文件)
若是兩個文件相等,他會返回:(有點不智能,沒法忽略空格和回車)
若是不相等,他會返回錯誤的地方:
很好用吧?
RT,就是啓動一個位於與cmd/.bat同文件夾內的程序(其實也能夠啓動不一樣文件夾裏的,不過要寫路徑)
代碼極短:
就是這樣。
你會發現:哎,個人代碼沒錯啊,爲何閃退了?
的確,你沒寫錯
但電腦默認執行完一個操做後自動跳出
因此看起來像閃退。
怎麼辦呢?
咱們可讓電腦完成操做後「暫停」一下。
看代碼:
電腦會停下來,直到你按一下鍵盤
這個很重要,由於循環什麼的在windows script中很難實現
咱們首先要在c++中引用頭文件:
#include<windows.h>
引用了這個頭文件,咱們就可使用system()函數
樣例:
system("pause");
在括號和引號中間寫你要執行的window語句便可
有的時候,你會發現對拍將兩個正確的輸出拍成錯的了,這是爲何呢?
很簡單,好比說上面那道線段樹,正解跑一秒,暴力4~5秒
但程序的命令行才無論你跑了幾秒,執行完一個立馬執行下一個
因此嘍,和可能你的數據還沒來得及輸出完,就已經被比較了
不錯纔怪呢!
因此咱們要寫Sleep()函數(注意S大寫!!)
暫停一段時間程序的運行,讓你的暴力好好跑一跑
怎麼實現呢?很簡單,看下面:
頭文件:
#include<windows.h>
實現方法:
咱們的實例仍是上面的線段樹1。
首先,咱們先將「正解」程序,暴力程序,數據生成器和對拍程序寫好,放在一個文件夾裏
#include<iostream> #include<cstdio> #include<windows.h> using namespace std; int main() { int cnt=0; while(cnt<=10) { cnt++; system("start sjmaker.exe");//啓動數據生成器 Sleep(500);//等待數據輸出(極限數據輸出大約0.5s) system("start baoli.exe");//啓動「正解」和」暴力「 system("start zhengjie.exe"); Sleep(10000);//這個時間取決於你寫的暴力的最壞時間 if(system("fc baoli.out zhengjie.out"))//若是出鍋,就停下來 { system("pause"); break; } } }
OK, Debug完成!你能夠去寫別的題了,只要時常回來看一看有沒有bug就好了。
固然,咱們不會強制你使用c++引用系統命令,咱們也能夠直接用windows批處理文件來解決。
首先,先說明一下,.bat批處理文件就是windows命令,只不過放到了記事本里,又改了一下後綴名。。。
這裏先補充一個操做:windows中的暫停操做(其實不是暫停,不過和暫停一個效果):
咱們用這條語言來掛起程序一段時間
1.首先,咱們新建一個文本文檔,在裏面編寫系統語言:
(此處爲源代碼,和c++中基本一致)
2.而後將文件後綴名改成.bat
運行後便可看到結果
你可能會問:這不是有bug麼?只能運行一次???
的確,這點不太友好,由於windows命令提示符中不支持循環。。。
因此,你有兩條路可走,一條是在c++的循環中 system(「start duipai.bat」);
(與其這樣還不如在c++裏直接寫呢。。)
還有一種是自調用bat(有些危險,手滑勿用)
咱們在bat文件的最後一行寫下這樣一行代碼:
start duipai.bat
他就會在執行完上一個程序以後自動重複執行
頗有可能咱們經過對拍,已經知道這個的答案是對的了,可是,咱們有時候又擔憂卡時間,怎麼辦呢?
咱們可使用clock()函數
顧名思義,這是一個計時函數
他大致上是這樣的:
先定義變量:
clock_t start,stop;
在代碼的開始各跑一個clock()函數,好比說:
#include<time.h> #include<iostream> #include<cstdio> using namespace std; int main() { clock_t start,end; start=clock(); //這裏寫你的源代碼 //。 //。 //。 //。 end=clock(); cout<<end-start; }
這樣就讀出了時間(單位:毫秒ms)
固然,這樣跑是會佔用必定cpu時間的,若是發現略有超時(好比1000ms你跑了1050ms),別怕,正常狀況仍是能過的
固然,你們若是有更好的方法,歡迎私信聯繫