做OI題時的一些常用的常數優化小技巧
註意:本文所介紹的優化並不是算法上的優化,那個就非常復雜了,不同題目有不同的優化。筆者要說的只是一些實用的常數優化小技巧,很簡單,雖然效果可能不那麽明顯,但在對時間復雜度要求十分苛刻的時候,這些小的優化對於幫助你成功卡常也是十分重要的。那麽我們讓切入正題吧。
(1)inline放在自定義函數前
不要問為什麽,加就行了!額,這個東西好像可以讓你的函數有機會被計算機執行得稍微快一點,一般放在使用次數比較多的函數前,像check(),為sort()定制的CMP()等等,當然主函數前就不要放了。。。比如下面這個例子:
1 inline bool CMP(const int &a,constint &b){ 2 return a>b; 3 }
(2)register放在變量前
這個可以有機會把變量申請存儲在寄存器中,一般可以用來定義賦值次數較多的循環變量跑得飛快!
(3)++i比i++快
記住就行了,盡量用++i而不用i++,當然有特殊需要要用i++時除外。
(4)讀入優化(很重要!)
這是針對整數的。先介紹一下原理:讀入一個數時把它當作字符讀比當作一個數讀快,或者說用getchar(),gets()一類讀比用scanf("%d",&x)要快。而讀入字符時本身就是這樣讀的,當然就不用優化了。不要問為什麽!一般會自定義一個read()函數來讀取,寫法有很多,這裏貼上最常見的寫法:
1 inline int read() 2 { 3 int f=1,x=0; //f表示符號,1為正,-1為負 4 char ss=getchar(); 5 while(ss<‘0‘||ss>‘9‘){if(ss==‘-‘)f=-1;ss=getchar();}//跳過數字前的空格等字符 7 while(ss>=‘0‘&&ss<=‘9‘){x=x*10+ss-‘0‘;ss=getchar();}//讀到下一位ss,就把已讀到的乘10,相當於全部進一位,為存ss留出空間 9 return f*x; 10 } 11 //使用時直接x=read(),相當於scanf("%d",&x);
使用讀入優化在數據規模較小時優勢並不明顯,但在數據規模很大,比如上百萬時,使用讀入優化會比不使用的讀取速度快上幾倍。
(5)輸出優化
既然有讀入優化,自然也有輸出優化。只是輸出優化應用機會很少(一般只輸出幾個數),只有在需要輸出的答案較多時才可能會用到。原理同讀入優化,把原本為整數的答案轉為字符(串)形式後輸出。例如輸出一個int型變量x,一般會寫:
1 printf("%d",x);
而用輸出優化就是:
1 void put(int x) 2 {3 if(!x) return ; 4 put(x/10); 5 printf("%c",x%10); //因為得從最高位到最低位輸出,所以采用遞歸實現 7 } 8
(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,在實際操作中很有用處。
初次發表博文,希望能幫到大家,如有錯誤,敬請指正!
2018-08-15
做OI題時的一些常用的常數優化小技巧