OI程序常見的設計陷阱
宏定義的問題
有時候為了方便,我會大量使用宏定義。但是最近我發現下面這兩個宏定義老是出問題:
1 #define SET(x,a) memset(x,a,sizeof(x)) 2 inline void work(){ 3 SET(head,0),SET(vis,0),SET(dis,0x3f); 4 //do something 5 }
這個宏定義似乎在初始化的時候會莫名奇妙地出現一些問題。
另外,還有這個:
1 #define RP(i,a,b) for(register int i=a; i<=b; i++) 2 inline void work(){ 3 vector<int> ver; 4 //do something 5 RP(i,0,ver.size()-1){ 6 //do something 7 } 8 }
當你定義了一個 RP 的循環宏時,它的判斷會出一些問題。比如說在上面這個例子,如果ver.size()==0,那么正常的for就不會進入這個循環。但是宏定義之后它的判斷順序似乎發生了改變。
很奇怪吧?因此,有些宏還是不要亂用。
另外,宏和函數不同。它相當于是讓編譯器對源碼進行自動替換,而宏的語法如果不加注意,則會出錯。舉個例子,如果你想把二維坐標映射到一個整數,用宏ID(a,b) = a * M + b 來實現,那么這個語法肯定有問題。假設坐標從0開始,你想得到右下角ID(M - 1, M - 1),那這個宏會把它變成M - 1 * M - M - 1 = -M - 1。正確的語法應該為ID(a,b) = (a) * M + (b)。
不要亂卡常
網上有一些奇奇怪怪的卡常方式,比如說把i++寫成i=-~i,或者用大量的逗號鏈接若干個語句。對于前者,這個二進制優化其實并不如想象中的那么出色,對速度的提升不明顯。(雖然我本人也喜歡把i++寫成++i以提高速度)對于后者,雖然逗號運算符在一定程度上可以提高效率,但有些時候會出現一些奇怪的問題。我記得很久以前,我喜歡把很多語句用若干個逗號連成一條;但是有時候,程序可能只會其中的部分幾條,剩下的會被忽略!我不知道是否真的有這個問題,但是我的建議還是不要亂用逗號。
注意空間復雜度
在學習任何一個算法或數據結構時,一定要翔實記錄它的空間復雜度。比如說,trie樹的空間復雜度是多少?一般要開多大?用鏈式前向星存無向圖,是否把邊數組開成題給的兩倍?這些問題不注意,你也許可以通過樣例,但最后可能一分都拿不到。如果你是在網上評測,有時候評測機并不會反饋RE的信息,而是WA。如果不加注意,你會因為這個問題而調試很久。
另外,足夠大的空間也可以從一定程度上提高程序的運行速度。這應該和“空間大易于伸展手腳”是一個道理。
注意類型轉換
始終注意類型轉換。有時候即便你把所有的數值變量開成long long類型,你可能會在賦值的時候忘記把一個int類型的表達式進行轉換。這樣,表達式在被賦值前就會發生溢出。任何時候要牢記各種變量的類型,并時不時轉換一下。
另外,強制轉換的速度會相當慢。如果你想轉換成long long類型,直接在變量前乘上1ll;對于普通double類型,可以直接乘上1.0;對于更特殊的long double或是__int128類型,你就只能強制轉換了。
慎用STL
STL會節省很多時間,而且大部分時候它們除了常數大以外,由于封裝了很多函數,使得它們操作起來非常方便。但是有些時候,用STL很有可能會RE。當然,這是否需要“釋放內存”,或者一些更高級的處理方法,這我不得而知。
學會設計參數
不久前在寫矩陣快速冪的時候,發現“引用形參”比不引用形參要快得多。因此,當函數的參數是一個比較大的數據結構時,采用引用的寫法會加速很多。

浙公網安備 33010602011771號