關於狀壓DP列舉子集的方法與理解

2021-08-22 15:21:17 字數 1700 閱讀 8769

題外話:

剛才發現自己已經不記得如何列舉乙個狀壓集合的子集(因為之前本身就沒有怎麼理解列舉子集的方法完全就是背下來的所以忘掉很正常),所以寫下這篇部落格做個提醒或者叫做警示吧,很多東西還是要理解透徹不然會吃虧的。

希望這篇部落格可以對博友們一些幫助,當然如果有錯歡迎指出。

我們現在要列舉狀壓集合s

ss的子集,**實現:

for

(int s1=s;s1!=

0;s1=

(s1-1)

&s)

其中s1s1

s1就是我們列舉得到的子集,s2s2

s2是s 1s1

s1在s

ss內的補集,即s1∪

s2=s

s1∪s2=s

s1∪s2=

s 贅述如下:

現在來講一講為什麼是這樣的乙個列舉方法,先讓我們來舉乙個例子來模擬一下。

假設我們當前要列舉的是(

10110)2

(10110)_2

(10110

)2​的子集(子集仍然用s1s1

s1表示):

s 1=

(10110)2

→(

10100)2

→(

10010)2

→(

10000)2

→(

110)2→

(100)2

→(10)

2s1=(10110)_2\rightarrow(10100)_2\rightarrow(10010)_2\rightarrow(10000)_2\rightarrow(110)_2\rightarrow(100)_2\rightarrow(10)_2

s1=(10

110)

2​→(

1010

0)2​

→(10

010)

2​→(

1000

0)2​

→(11

0)2​

→(10

0)2​

→(10

)2​根據例子,我們發現按照上面**得到的結果是正確的,並且是把子集按照從大到小的順序列舉出來的。那麼接下來我們來談談這樣列舉的正確性。

首先,乙個集合它自己本身也是自己的乙個集合,所以我們從這個集合本身開始列舉。

既然是列舉,那我們就先考慮把當前列舉得到的子集先−1-1

−1,但是這樣做不能保證−1-1

−1後得到的狀態是原狀態的子集,但是我們注意到:根據與運算&的性質,我們不難發現如果兩個數a,b

,a

<

ba,b,aa,

b,a<

b,我們對這兩個數進行&運算,最後的結果一定是b

bb的子集,因為我們與運算&得到的結果,在二進位制中出現1

11的位,b

bb中一定也是111。

現在已經說明了這樣做確實得到了原集合的子集,但是還沒有說明我們已經列舉完了原集合的子集。

其實列舉子集就相當於在原集合的二進位制狀態下把一些1

11換為0

00,而我們每次−1-1

−1然後進行與運算其實就是在把當前子集的最右邊的1

11的右邊全部變為1

11,自己變為0

00,然後進行與運算把新增的1

11中不該出現的抹去,最後只剩下了原集合中存在的1

11了。

TYVJ4869 罪犯分組 狀壓DP列舉子集

資料範圍這麼小,很容易考慮到狀壓dp。預處理出所有情況的罪犯矛盾數並判斷哪些狀態是不合法的。之後就轉換為了乙個比較簡單的揹包dp問題。狀態轉移方程 f s m in f i 1 i 為s的子 集,且i xors 表示的集 合中矛盾 數不超過 k 問題似乎解決了,但是有乙個問題 如何高效列舉s的子集?...

動態規劃 習題 光棍組織(狀壓 列舉子集)

題目描述 mm 雖然一輩子只要乙個,但是也得早點解決。於是,n 個光棍們自發組成了乙個光棍組織 ruffian organization,by wind 亂譯 現在,光棍們打算分成幾個小組,並且分頭為 找 mm 事 業做貢獻 for example searching,hunting by wind...

列舉子集的幾種方法

程式設計挑戰競賽上156頁說了列舉組合和子集的幾種方法,我覺得挺好的,收藏一下 都是利用二進位制數的模型來進行列舉子集或者組合 下面列舉集合都是在二進位制位上進行列舉。列舉k個數的子集 void eunmset int k 用k個二進位制位數列舉k個狀態 當k等於3時,即eunmset 3 輸出 0...