debug?用對拍!


很多人考noip之類的比賽永遠會發生一些奇怪的問題

比如說下面這兩位(來自我的兩位學長)

sliver n:spli,考得怎樣啊?

spli:就那樣啦,day1T1沒推出來規律,別的還好

silver n:看來你省一高分預定啊

幾周后。。。

silver n:分出來了嗎?

spli:出來了。。。

silver n:怎么了?

spli:別提了,day2T2掛了,沒拿上省一

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),別怕,正常情況還是能過的

當然,大家如果有更好的方法,歡迎私信聯系


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM