debug?用對拍!

不少人考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庫),但容易出鍋(我就出過幾回))

(注意,本篇文章默認使用windows系統!!!)

首先,咱們先掌握幾個前置技能:

1.編寫你的認爲是正解的程序和暴力程序(你要不會我也沒辦法了)

2.數據生成器(也就是隨機數)

先說隨機數

相信你們通常用隨機數都是用

#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語言)

這個你們可能不太熟悉,

不過不要緊,我會把可能用到的都講一遍。

(1)文件比較操做:

也就是比較兩個文件,判斷這兩個文件是否相等

咱們通常用命令提示符(cmd.exe)操做(也能夠寫成.bat批處理文件)

若是兩個文件相等,他會返回:(有點不智能,沒法忽略空格和回車)

若是不相等,他會返回錯誤的地方:

很好用吧?

(2)啓動程序操做

RT,就是啓動一個位於與cmd/.bat同文件夾內的程序(其實也能夠啓動不一樣文件夾裏的,不過要寫路徑)

代碼極短:

就是這樣。

(3)暫停操做

你會發現:哎,個人代碼沒錯啊,爲何閃退了?

的確,你沒寫錯

但電腦默認執行完一個操做後自動跳出

因此看起來像閃退。

怎麼辦呢?

咱們可讓電腦完成操做後「暫停」一下。

看代碼:

電腦會停下來,直到你按一下鍵盤

 4.在c++語言中引入windws script語言:

這個很重要,由於循環什麼的在windows script中很難實現

咱們首先要在c++中引用頭文件:

#include<windows.h>

引用了這個頭文件,咱們就可使用system()函數

樣例:

system("pause");

在括號和引號中間寫你要執行的window語句便可

5.Sleep掛起操做

有的時候,你會發現對拍將兩個正確的輸出拍成錯的了,這是爲何呢?

很簡單,好比說上面那道線段樹,正解跑一秒,暴力4~5秒

但程序的命令行才無論你跑了幾秒,執行完一個立馬執行下一個

因此嘍,和可能你的數據還沒來得及輸出完,就已經被比較了

不錯纔怪呢!

因此咱們要寫Sleep()函數(注意S大寫!!)

暫停一段時間程序的運行,讓你的暴力好好跑一跑

怎麼實現呢?很簡單,看下面:

頭文件:

#include<windows.h>

 實現方法:

好啦,前置技能講完了,下一步我來說實例!

咱們的實例仍是上面的線段樹1。

首先,咱們先將「正解」程序,暴力程序,數據生成器和對拍程序寫好,放在一個文件夾裏

下面我給出對拍程序的源代碼(注意,數據生成器,「暴力」和「正解」務必提早編譯(也就是要exe)):

#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++的話......

固然,咱們不會強制你使用c++引用系統命令,咱們也能夠直接用windows批處理文件來解決。

首先,先說明一下,.bat批處理文件就是windows命令,只不過放到了記事本里,又改了一下後綴名。。。

這裏先補充一個操做:windows中的暫停操做(其實不是暫停,不過和暫停一個效果):

 

咱們用這條語言來掛起程序一段時間

下面咱們來看實例:

 1.首先,咱們新建一個文本文檔,在裏面編寫系統語言:

 

(此處爲源代碼,和c++中基本一致)

2.而後將文件後綴名改成.bat

運行後便可看到結果

你可能會問:這不是有bug麼?只能運行一次???

的確,這點不太友好,由於windows命令提示符中不支持循環。。。

因此,你有兩條路可走,一條是在c++的循環中  system(「start duipai.bat」);

(與其這樣還不如在c++裏直接寫呢。。)

還有一種是自調用bat(有些危險,手滑勿用)

咱們在bat文件的最後一行寫下這樣一行代碼:

start duipai.bat

他就會在執行完上一個程序以後自動重複執行

(warning!!前面的暫停時間必需要開大,不然快速的重複執行會讓你死機!!(你的手都來不及關))。

番外篇:clock()函數

頗有可能咱們經過對拍,已經知道這個的答案是對的了,可是,咱們有時候又擔憂卡時間,怎麼辦呢?

咱們可使用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),別怕,正常狀況仍是能過的

固然,你們若是有更好的方法,歡迎私信聯繫

相關文章
相關標籤/搜索