集合列舉子集 學習筆記

2022-10-09 13:48:12 字數 1039 閱讀 1446

有乙個集合,請輸出它的所有子集。

子集,即為被這個這個集合包括的所有集合,包括空集。那麼顯然,假如有 \(n\) 個元素,那麼有 \(2^n\) 個子集。如何列舉子集呢?

首先有乙個顯然的方法:用 \(2^n\) 的 dfs 列舉。但這樣有乙個弊端:時空較大,而且比較麻煩。比如乙個動態規劃,你每次轉移都要跑一遍 dfs ,時空開銷很大,而且處理不方便。那麼就有另外一種方法出現:迴圈列舉。

首先放**:

for(int j=st;j;j=(j-1)&st) j2=st^j;
st就是要列舉的集合,j就是子集,j2就是j相對於st的補集。這裡我們認為集合是乙個二進位制數中所有元素 \(1\) ,乙個 \(1\) 代表乙個元素。為什麼這樣可以呢?

模擬一遍過程:st=1101,我們按照迴圈順序模擬過程。

\(1101\)​

\(1100\)

\(1001\)

\(1000\)

\(0101\)

\(0100\)

\(0001\)

我們假定 \(0000\) 也在元素中,然後按照一定規律排列子集:

\[1101\;\;\; 0101

\]\[1100\;\;\; 0100

\]\[1001\;\;\; 0001

\]\[1000\;\;\; 0000

\]有沒有發現規律?每一行,後三位都是相同的。所以這個演算法的本質算是,對於每乙個 \(1\) ,先把這一位設定為 \(1\) 列舉後面所有位,再把這一位設定為 \(0\) 列舉一遍,然後再往前面遞迴。因為減 \(1\) 可以看做把最後乙個 \(1\) 設定為 \(0\) ,後面所有設定為 \(1\)。而且這個做法是從大到小列舉子集,著實十分優美。但要注意的是這種辦法沒法列舉空集,需要手動計算。

列舉子集在一些位運算的題目中可能會涉及,而這種方法可以很好地減少**複雜度以及時空複雜度,同時也是一種很好的思想。

Greetings (列舉子集 dp)

題目 題意 給n個信 信封有長寬和數目 問在找最多k種信封型別的時候,最少浪費多少紙 思路 看了看題解,深以為然,這個思路很巧妙,也許是我太菜,沒細想。我們首先二進位制列舉計算出,每種狀態下,這些信合用同一種信封時的浪費數目。然後通過列舉子集,dp i j 代表 i 種信封,現在已經裝了 j 集合的...

馬的管轄 列舉子集

在中國象棋中,馬是走日字的。乙個馬的管轄範圍指的是當前位置以及一步之內能走到的位置,如果一匹馬的某個方向被蹩馬腳,它就不能往這個方向跳了。自行腦補 那麼問題來了,在乙個 n m 的棋盤內,如何用最少的馬管轄住所有 n m 個格仔。比如 n m 3 時,最少要用 5 隻馬才能管轄所有棋盤,一種可能的方...

列舉 子集生成法1

紫書p188 7.3子集生成 1 增量構造法 1 include2 include3 using namespace std 4int aim 100 5 int psd 100 核心 如下 6int subset int cur,int n,int last 必須使用last元素記錄上層函式新增的...