位運算之ACM優化運用

2021-04-24 17:32:22 字數 3614 閱讀 2060

文章**

文章組織:

1、基本操作符

2、需要注意的問題

3、一些小應用

4、針對具體題目的應用

1> 搜尋類

2> 字串類

3> 其他類基本操作符

與 &或 |

異或 ^

左右移位 << / >>

取反 ~

需要注意的問題:

1、優先順序,這是個非常嚴重的問題,在進行位運算的時候優先順序太容易被忽略掉了

尤其要注意的:

移位運算子, 單目的取反運算子~的優先順序比比較運算子高。但是,&, |, ^的優先順序是比比較運算子低的!,這點一定要注意

如6 & 6 > 4的結果是0, 而不是 true; (6 & 6) > 4的結果才是true,所以要注意勤加括號

2、速度

位運算的速度是非常快的,你甚至可以忽略他的耗時,但是畢竟操作肯定是有耗損的。

所以,應該盡量使用

^=, |= ,&= 之類的操作,比如說a ^= b, 速度比 a = a ^ b快,因為前者是直接在a上進行操作,而 a = a ^ b的第二個a,是乙個a 的副本,可見在操作中程式對a複製了一次,運算的結果又對原來的a做了一次賦值

3、範圍(要當心移位運算時發生範圍溢位)

常用操作:

1、交換兩個元素的值:a ^= b ^= a ^= b,不用借助第三個變數

2、不查表統計1的數量

int bitcnt(unsigned int n)

3、迴圈位移

unsigned shift(unsigned now, int n, int n)

now &= base;

return now;

}4、k位取反

a=~(~a^(a<優勢:

速度快,

節省空間,又導致了速度快

poj的題目上:

大類1:搜尋

此處不講題目,只講位運算是怎樣在這些題中實現和應用的。由於搜尋題往往是基於對狀態的操作,位運算往往特別有效,優化之後的效果可以有目共睹。

例1、poj 1324

根據題目,確定了對狀態的表示之後(記錄當前狀態的蛇頭x, y值與剩下部分的運動狀態),一般容易想到剩下部分的運動狀態用乙個陣列(比如x[n-1], n為蛇節點數,n[0]為第二個節點的運動趨勢)去表示,且乙個陣列元素的值為0,1,2,3,即四個方向,蛇每次移動,這個方向陣列需要更新一次,更新是很簡單的,除了n[0]更新之外,剩下的 n[i]等於上一輪的n[i-1](i>1);如果用陣列,就必須用乙個迴圈去更新它,這樣會導致程式速度不是很理想,所以可以想到用位運算去模擬,簡言之,就是把原來的乙個陣列壓縮的乙個變數上去儲存,原來任何乙個陣列元素的值範圍0~3, 用二進位制表示就是 00 ~ 11,最多占用2位,這樣,我們就可以通過移位運算,和或運算去實現所以操作。

基本技巧: 如果給你個乙個方向序列 a0, a1, a2, a3, .... a(n-1),放到變數b中,且以最低位表示an,可以用如下迴圈

b = 0;

for(i=0; i如果要從b中復原,則可使用 >> 的辦法

for(i=0; i> (i*2)) & 3; 

移位會將低位元素除掉,而 & 3則是把移到低位的元素取出。

更高效的方法是(兩種方法的前提都是不改變b的值)

t = b;

for(i=0; i>= 2;

}高效的原因如帖子開頭所述。

在表示狀態的位段操作中,最好將變數設為unsigned int (short)型,因為有符號型(負數的最高位(符號位)為1)的 >>操作加在負數上時會在右邊引入1而不是0, 這基本上不會是你想要的操作。

例2、poj 3460 booksort

這題可以在對書進行交換的時候應用位運算,也許你認為用陣列去模擬更為直觀,但是看看下面的實現,也許你就不那麼認為了。

書最大編號15,

這樣就開乙個64位整形,每4位表示一本書

首先預處理,計算出兩張表

extract[i][j] 和 clear[i][j]分別表示把書架上第i到j本書取出或清空,顯然在i到j一段,位上全是1,而其他部分是0,而clear[i][j]就是extract[i][j]的取反

計算方法如下:

typedef unsigned long long longint;

const longint base = 15;//這是必須注意的,普通的常數15是整形,在以下的操作中會越界

for (i = 0; i < 15; i++)

}計算出這些以後,交換書就變得異常簡單

inline void transposition(longint &cur, longint &next, int &i, int &k, int &j)

先把i到j全部清空後的值賦給next,把 i, k段的書取出,移到高位(差值為(j - k) * 4),同理,再把k+i到j段的值取出移到低位。

如此清楚明了,而如果用陣列的話,10+行的語句是少不了的,而且容易搞錯。

例3:hoj 八數碼問題

這題用位運算我倒是沒有達到更快,空間稍微節省了些。

另外,這題的測試資料很強的說,贊乙個。

用64位的整形去表示整個狀態,其中每4位表示乙個數碼(0~8),如果是用char陣列,則用9個,用了72位。

取出某一位和清空某一位的掩碼事先要計算好

這樣做比用乙個整形數如 123480567 之類的去表示更有優勢,因為顯然取出某一位的速度非常快,不需要 % 10, /10之類耗時的操作,如果這樣實現的話就不要用stl的map了,我無聊地試了次,相當之慢。還是老老實實康托展開的好。不過和開char陣列的速度差不多的說(雖然說賦值時間大了,但畢竟別人是陣列,呵呵,要是有每4位做乙個單位的陣列就好了——不要跟我說用struct裡面定義的位段啊,那相當的慢)。

最近還做了乙個poj 2286,可以用位運算,但貌似有些麻煩,沒用,結果被乙個用pascal的小子超在前面,哪天狀態好了要把這題改用位運算去做,就不信刷不到 0ms.

更新中...

大類二:字串的hash與查詢

大家都知道rabin-karp演算法吧,其實rabin-karp實現後慢的乙個原因是太多的模運算了,實際上,我們可以通過位運算隱式地取模

原鏈結如下:http://www-igm.univ-mlv.fr/~lecroq/string/node5.html#section0050

它開了乙個int型,每次用移位代替乘法,又由於整形是有範圍的,但移位超過了範圍,效果就相當於做了一次 mod int_max的操作。這樣改進後速度提高了一倍。

poj 2513 colored sticks就可以考慮使用此種方法的hash,他的衝突也是非常小的。當然有更高效的的字串位運算hash演算法,由於某些原因,我就不在這說了。

//待續

大類三:

巧妙運用位運算

本文旨在通過兩道巧妙運用位運算的題,認識位運算的魅力 題意 給定兩個序列 a,b 求 a,b 的最長公共子串行 a b le 10 5 時限 5s 目前求任意兩序列的最長公共子串行,是沒有複雜度低於 o a cdot b 的演算法的 回顧經典的 o a cdot b f max f f f a i ...

位運算概念與運用

程式中的所有數在計算機記憶體中都是以二進位制的形式儲存的,位運算就是直接對整數在記憶體中的二進位制位進行操作的運算 按位與 and 按位或 or 按位異或 xor 按位取反 not 左移 shl 帶符號右移 shr 與運算 是雙目運算子,二進位制對應位,同為1則為1,否則為0 例如 二進位制數101...

位運算的實踐運用

判斷乙個數的奇偶 var n 2n 1 0 var n 1n 1 1向下取整 1.2 1 1.8 1 1.8 0 1 1.14 0 1 1.14 0 1 1.14 0 1 不可對負數取整 2的倍數乘除法 1 2 4 1的2進製左移2位,即乘2的2次方 8 2 2 8的2進製右移2位,即除2的2次方比...