OI常用的常数优化小技巧
注意:本文所介紹的優化并不是算法上的優化,那個就非常復雜了,不同題目有不同的優化。筆者要說的只是一些實用的常數優化小技巧,很簡單,雖然效果可能不那么明顯,但在對時間復雜度要求十分苛刻的時候,這些小的優化對于幫助你成功卡常也是十分重要的。那么我們讓進入正題吧。
(1)inline放在自定義函數定義前
不要問為什么,加就行了!額,這個東西就是內聯函數,好像可以讓你的函數有機會被計算機執行得稍微快一點,一般放在使用次數比較多的小函數前,像二分會常用到的check(),為sort()定制的CMP()等等,但是遞歸函數和較大的函數編譯器會自動忽略,當然主函數前就更不要放了。。。比如下面這個例子可以用:
inline bool CMP(const int &a,const int &b){ return a>b;}
(2)register放在變量定義前
這個可以有機會把變量申請存儲在CPU寄存器中成為寄存器變量然后跑得飛起!但是CPU寄存器的內存是很小的,因此一般只用來定義賦值次數較多的單個變量(比如,循環變? ? ? ? ? ? ?量),而且似乎是不能定義成為全局變量的。
(3)++i比i++快
記住就行了,盡量用++i而不用i++,當然有特殊需要用i++時除外。
(4)讀入優化(很重要!)
這是針對整數的。先介紹一下原理:讀入一個數時把它當作字符讀比當作一個數讀快,或者說用getchar(),gets()一類讀比用scanf("%d",&x)要快。而讀入字符時本身就是這樣讀的,當然就不用優化了。不要問為什么快!一般會自定義一個read()函數來讀取,寫法有很多,先貼上最常見的寫法:
inline int read() { int f=1,x=0; //f表示符號,1為正,-1為負char ss=getchar(); while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}//跳過數字前的空格等字符while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}//讀到下一位ss,就把已讀到的乘10,相當于全部進一位,//為存ss留出空間return f*x;
}
//使用時直接x=read(),相當于scanf("%d",&x);
然而裝逼是沒有止境的,你也可以這樣寫:
inline int read() { int X=0,w=0; char ch=0;//這個ch一定要賦初值,除了‘0’~‘9’(注意這里是字符)什么都可以,不然可能會出鍋的。。。 while(!isdigit(ch)) {w|=ch=='-';ch=getchar();} while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar(); return w?-X:X; }?首先問題是isdight()是什么玩意兒?它是定義在cctype頭文件中的一個函數,用于判斷是否是整數。w|=ch=='-',其實相當于w=w|(ch=='-'),|(或)是C語言中的一個位運算符,如果a和b中有一個為1,結果就為1。w一開始等于0,而當ch=='-'成立時,表達式的值為1,0的二進制|1的二進制結果就是1,w就被賦值為1了,表示有負號。(X<<3)+(X<<1)=X*8+X*2=X*10,原因見后面。^(異或)也是位運算符,筆者沒有仔細研究,不過可以肯定的是一個char型數字^48(0的ASCLL值)就等于它的int型數字,所以有(ch^48)一用法。要注意的是位運算符比加減乘除的優先級都要低,所以一定要加括號。
? ? ? 使用讀入優化在數據規模較小時優勢并不明顯,但在數據規模很大,比如上百十萬時,使用讀入優化會比不使用的讀取速度快上幾倍,為你成功卡常&暴力騙分爭取寶貴的時間。
? ? ? 事實上還有一種比讀優都要快10%的讀優,就是用fread(),但是一般情況下不至于這么苛刻吧,所以筆者就不多說了(其實是筆者不會)。
(5)輸出優化
既然有讀入優化,自然也有輸出優化。只是輸出優化應用機會很少(一般只輸出幾個數),只有在需要輸出的答案較多時才可能會用到。原理同讀入優化,把原本為整數的答案轉為字符(串)形式后輸出。例如輸出一個int型變量x,一般會寫:
printf("%d",x);? 而用輸出優化就是:
inline void print(int x) { if(x<0){putchar('-');x=-x;} if(x>9) print(x/10); putchar(x%10+'0'); }(ps:由于筆者的粗心,輸出優化代碼前面打錯了,特此更正)
(6)使用位運算符<<與>>
這兩個東西是C語言中的位運算符,什么意思呢?不會的可以百度一下,簡單來說就是一個數在二進制狀態下向左(右)移幾位(超出的位數舍棄)后的值。比如1<<2,意思是把1的二進制左移2位后得到的值。我們知道1的二進制是1(2),左移2位,就是100(2),也就是4。那么8>>1的值是多少呢?8=1000(2),右移一位就是100(2),也就是4。也許你會驚奇地發現a<<b就等于a*2^b,a>>b就等于a/2^b。沒錯!這就是我們要用到它的地方。當你寫a=a/2時,你也可以寫成a=a>>1;a=a*2也可以寫成a=a<<1,等等。
那么,為什么我們非要用這個位運算符呢?因為在C語言中,位運算比起加減乘除等屬于較底層的操作,加減乘除其實也是通過位運算實現的,算是一種把底層操作更高級地“打包”起來,就像高級語言是由機器語言轉化而來的,執行時仍然要編譯為機器語言,這中間當然會花費一些不必要的時間和空間,因此越底層的操作往往越快。并且,我們可以用1<<n很方便地表示2^n,在實際操作中很有用處。
-->最后,筆者還想提醒一點,據學長所說inline和register是兩個非常玄學的東西,有時可能還會造成負優化。。。(好了我就是想說被卡了別找我)
?
初次發表博文,希望能幫到大家,請多多指教.
2018-08-15
轉載于:https://www.cnblogs.com/gosick/p/9484087.html
總結
以上是生活随笔為你收集整理的OI常用的常数优化小技巧的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 4412 PWM
- 下一篇: C++面试笔记(2)