關於OI中簡單的常數優化


有些東西借鑒了這里qwq

1.IO(istream/ostream) 輸入輸出優化

之后能,在賽場上常見的幾種輸入輸出:

  輸入:

  $1.cin$ 呵呵,不說什么了,慢的要死。大概$1e8$個數要讀1分鍾左右

  $2.scanf, \_ \_ builtin \_ scanf()$ $scanf$ 其實還不算太快,但是$\_ \_ builtin \_ $在$NOIp$賽場上會$CE$ 

  $3.read()$ 美其名曰:讀入優化(反正本寶寶不會),在各大神犇的提交記錄以及題解上隨處可見,親測確實比$scanf$要快許多

  代碼大概長這樣: $by\ Young\ Neal$

int getint(){
    int x=0,f=0;char ch=getchar();
    while(!isdigit(ch)) f|=ch=='-',ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return f?-x:x;
}

  $4. fread$ 歡迎來自$AK$爺的飛速文件讀入輸出

struct file_io{
    #define isdigit(ch) ((ch) >= '0' && (ch) <= '9')
    char inbuf[1 << 25], *pin, outbuf[1 << 25], *pout;
    int stk[20];

    file_io(): pout(outbuf) {fread(pin = inbuf, 1, 1 << 25, stdin);}
    ~file_io() {fwrite(outbuf, 1, pout - outbuf, stdout);}

    inline void getint(int &num){
        bool neg = 0; num = 0;
        while(!isdigit(*pin)) if(*pin++ == '-') neg = 1;
        while(isdigit(*pin)) num = num * 10 + *pin++ - '0';
        if(neg) num = -num;
    }

    inline void putint(int num){
        static int *v = stk;
        if(!num) *pout++ = '0';
        else{
            if(num < 0) *pout++ = '-', num = -num;
            for(; num; num /= 10) *v++ = num % 10;
            while(v != stk) *pout++ = *--v + '0';
        }
    }

    inline void nextline() {*pout++ = '\n';}
} fio;
#define getint(num) fio.getint(num)
#define putint(num) fio.putint(num)
#define nextline() fio.nextline()

  

  輸出:

  $1.cout$ 呵呵,和$cin$一個樣

  $2.printf , \_ \_ builtin \_ printf$ 已經比上面那個快多了,但是一樣,$\_ \_ builtin \_ printf$會$CE$

  $3.puts("")$ 對於已知字符串來說,能用 $puts()$不用$printf()$ 但是$puts("")$輸出之后會換行

  $4.fio$見上面 (STO GhostCai)

當然了,在$NOIp$范疇內,輸入輸出量並不算太大,多數$printf(),scanf()$就可以了,

除非$……$

你是要暴力碾標算然后再去$diss$一頓出題人數據水的神犇$……$

這時候讀優就必不可少了。

2.內存優化

  某$shadowice1984$大佬最擅長的東東,以下引用$shadowice1984$給本寶寶講課時候的話:

“我們的$CPU$要對某一個數進行計算的時候,會先在一級緩存中找這個數的地址要是找到了,直接揪過來~~槍斃~~進行計算,速度很快的,

但是一級緩存能儲存的東西很少,就是幾個$int$,如果沒有找到,成為'一級緩存未命中',但是,這時候,我們還有一個二級緩存和三級緩存,同樣,也很很快,

而‘三級緩存未命中’之后,卻要去內存里找這個東西了,這里面會經歷虛擬地址與物理地址的互相轉換然后還有$……(\text{此處省略})$然后耗時巨大(相對於從緩存直接調用來講)。

這是后,我們就會稱為$cache\ miss$,而如果我們的程序因為$cache\ miss$的次數太多而導致常數因子看似特別大(理論復雜度正確但是就是$tle$),就習慣的成為卡$cache$”

  當本寶寶聽完這段話之后,

  

哇$……$真是一個暴力碾標算的好方法

那么$……$我們怎么才能減少$cache\ miss$呢?

  $1.$保證內存的連續訪問

  這就是為什么本寶寶鄰接鏈表寫的每次都比別人慢上差不多一倍$qwq$ ,因為現在只有本寶寶用結構體寫了啊啊啊啊啊啊!

  $2.$多個$for$循環可以適當調整順序

  最明顯的就是矩陣乘法和$Folyd$算法了,對於$folyd$ $(eg. \ NOIp2016 \text{換教室})$

for(int i=1;i<=n;i++)
  for(int j=1;j<i;j++)
    for(int k=1;k<=n;k++)
      d[i][j]= d[j][i]=min(d[i][j],d[i][k]+d[k][j]);

 

for(int k=1;k<=n;k++)
  for(int i=1;i<=n;i++)
    for(int j=1;j<i;j++)
      d[i][j]=d[j][i]=min(d[i][j],d[i][k]+d[k][j]);

  親測第二種寫法會比第一種寫法快好多呢。

  矩陣乘法同理,

for(int k=1;k<=n;k++)
  for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
      c.num[i][j]+=a.num[i][k]*b.num[k][j];

  把$k$這一維放到外面明顯比放到里面要快

 

3.register 與 inline

  $register$在$for$里面真的會快一點,用法就是$for(register int i=1;i<=n;i++)$

  $inline$ 只能用在沒有遞歸的函數里,其實手動$inline$是個很好的東西,但是不一定。

  這兩個有的時候會造成負優化,這就呵呵了。

  

 

4.(僅限於$NOIp$等不開$O2$的賽事上) 手寫$stl$

  $stl(\  C++ \ Standard\ Template \ Library  \ )$  確實特別好用,也是$C++$的精華所在,

  可以為程序員們節省很大的時間,

  但是,在不開$O2$的情況下,會因為種種原因慢的要死,

  盡量背過一些簡單的數據結構,能少用就少用。

 

5.玄學(信仰)優化:

(1). 打表優化

 如果有的寫的非正解,但是又想拿高分,全打表得話會超過代碼長度限制,這是后可以部分打表,記得做過一道題

    對於30%的數據,滿足n<=500;

    對於100%的數據,滿足n<=1000;

  然后我和$shadowice1984$都會一個$On^{3}$的方法,顯然肯定過不了$1000$

  之后,本寶寶放棄了,拿了$30$分去一邊哭去了,

  $shadowice1984$,$n^{3}$信仰過$500$,然后又信仰的打了一下$n=995$到$n=1000$的表

  然后就$……$ 然后就 $A$ 了

  

  數據范圍內,只打極限數據,放心,出題人一定會很毒瘤的

  還有優化打表的方法:查分優化,二次查分優化,$26$進制壓縮($eg.$樹的平均路長問題)

(2).女裝優化(親測有效)

  女裝可以大大的減少$bug$和常數,真的親測有效,模擬賽的時候$t$的東西,**后再測就過了。。

(3).神犇優化(親身經歷) 

  有一次網上打$nowcoder$的比賽,然后和旁邊的神犇代碼比較一下只有變量名不同(才不是互相抄的呢)

  然后$……$人家神犇就過了,本寶寶就$T$了,

  平時做題的時候,本寶寶經常出現用時和神犇差距很大,

  自己寫線段樹都已經不記錄$l,r$了,還比那些開結構體記錄$l,r$的神犇慢$……$ 

  真的是人菜常數大,真的是$……$

  ~~所以想暴力碾標算的話,先要成為神犇~~

  

(4).信仰優化

  $srand(1926****)$ $srand(\text{cp或神犇生日})$

  $eg.\ srand(20020902) \ \ srand(20020224)  $

  (左cp右神犇)

  然后當你

 

printf("%s",rand()%2?"Yes":"No")

 

的時候會增大$AC$率的 ($NOIp2017\ \ D1\ \ T2$)


希望有用(光速逃~)

 


免責聲明!

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



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