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