求自己總共有三種方式:
增量構造
位向量
二進制
首先假設集合A中有n個元素,而且是非重集,一個下標唯一對應一個元素,那么求A的子集就變成了求0~n-1的子集。這個思想對於所有的三種方式都是通用的。
第一種增量構造法的思想是,每一次都從0~n-1中挑出一個元素來,每挑一次,就是一個集合。然后再挑元素進入這個集合,但是這次挑選元素的時候,必須比之前的那個元素大。
下面是代碼實現:
//假設后一個非可重集合P,里面的元素各不相同,現在要從中挑選出它的所有子集來 //這個問題可以轉換成挑選出P數組的下標的所有子集。即若P中有n個元素,那么就挑選出0~n-1之間的所有子集來 //以上的分析適合於所有的子集生成算法 //增量構造法的本質是這樣的,每次從0~n-1 中挑選出一個元素來,每挑選一次,就是一個子集。然后再給這個已經挑選出來的子集中挑選元素,這次挑選出來的元素 //必須比之前的元素要大 #include<cstdio> using namespace std; const int maxn = 100 + 10; int ans[maxn]; int n; void print_sub_set(int cur) { for(int i = 0; i < cur; i++) { printf("%d ",ans[i]); } printf("\n"); int s = cur ? ans[cur - 1] + 1 : 0; for(int i = s;i < n; i++) { ans[cur] = i; print_sub_set(cur +1); } } int main() { n = 3; print_sub_set(0); return 0; }
第二種方式是位向量法,如上一種方式所述,問題已經被轉換成求0~n-1的子集,這時再轉換一次,轉換為求一個長度位n位的向量B[i],當b[i]為1時,代表i在這個集合中。在實現的時候,采用了遞歸的思想,每次都決定每一位的歸屬。
下面是代碼實現:
//在上一篇說過,問題轉換成了枚舉0~n-1之間的所有的子集 //在位向量法中,還需要再次去轉換,即將問題轉換為構造一個有n位的向量,當b[i]為1的時候,表示i-1在此集合中,即元素A[i - 1]在集合中 #include<cstdio> using namespace std; const int maxn = 100 + 10; int B[maxn]; int n; void print_subset(int cur) { if(cur == n) { for(int i = 0;i < n;i++) { if(B[i]) printf("%d ",i); } printf("\n"); } else for(int i = 0; i <= 1; i++) { B[cur] = i; print_subset(cur + 1); } } int main() { n = 3; print_subset(0); return 0; }
最后是二進制法,二進制法的本質和位向量法是一致的,只不過位向量法中的向量B[],變成了一個整數轉換為二進制時有n位的整數,這樣就可以從0枚舉到1<<n - 1,然后再去依次判斷各個位的情況
下面附上代碼:需要注意的一點就是如何判斷各位的情況,就是x&1<<i
//二進制法的本質和位向量法是一致的,都是構造一組向量 //但是二進制法使用的是位運算的方式 #include<cstdio> using namespace std; int n; //const int ALL_BITS = 1<<n - 1; void print_subset(int x) { for(int i = 0; i < n;i++) { if(x & (1 << i)) { printf("%d ",i); } } printf("\n"); } int main() { n = 3; for(int i = 0;i < 1<<n;i++) { print_subset(i); } return 0; }
經過比較這三種方式的效率,增量構造的效率最高,二進制次之,位向量最慢
當輸入達到26的時候,增量構造法11s,二進制15s,位向量20s
但是編程的復雜度而言二進制最簡單,增量構造和位向量一致
&符號代表所有的位一起與。
