子集生成算法:
給定一個集合,枚舉所有可能的子集。暫時討論沒有重復元素的情況。
1 增量構造法
一次選出一個元素放到集合中,和前面不同,由於A中的元素個數不確定,每次遞歸都要輸出當前集合。另外遞歸邊界也不需要顯式確定-如無法添加元素,就不會遞歸了。
注意:定序,規定集合A的所有元素的編號從小到大排列,就不會把集合{1,2}按照{1,2}和{2,1}輸兩次了
代碼:
1 void print_subset(int n,int*A,int cur) 2 { 3 for(int i = 0;i<cur;i++) 4 { 5 cout << A[i] << " "; 6 } 7 cout << endl; 8 int s = cur? A[cur-1]+1 : 0;//確定當前元素最小可能值 9 for(int i = s;i<n;i++) 10 { 11 A[cur] = i; 12 print_subset(n,A,cur+1); 13 } 14 }
將代碼稍作修改后就可以輸出P中的元素了,因為把1~n的子集放在A內,A就可以當P的子集的元素的索引了
代碼:
1 void print_subset_2(int n,int*P,int*A,int cur) 2 { 3 for(int i = 0;i<cur;i++) 4 { 5 cout << P[A[i]] << " "; 6 } 7 cout << endl; 8 int s = cur?A[cur-1]+1:0; 9 for(int i = s;i<n;i++) 10 { 11 A[cur] = i; 12 print_subset_2(n,P,A,cur+1); 13 } 14 }
2 位向量法
構造一個位向量B[i],而不是直接構造子集A本身,其中B[i]=1,當且僅當i在子集A中。
代碼:
1 void print_subset_3(int n,int*B,int cur) 2 { 3 if(cur==n) 4 { 5 for(int i = 0;i<n;i++) 6 { 7 if(B[i]) cout << i << " "; 8 } 9 cout << endl; 10 return; 11 } 12 B[cur] = 1; 13 print_subset_3(n,B,cur+1); 14 B[cur] = 0; 15 print_subset_3(n,B,cur+1); 16 }
所有元素判斷完是否選擇后才是一個完整的子集。現在解答樹上有2047個節點
這是一個n+1層二叉樹(cur從0到n),第0曾有1個節點,第二層有兩個節點(第一位是否選擇),...,總數為1+2+...+2^n=2^(n+1)-1。
也可改進成輸出P的形式
代碼:
1 void print_subset_4(int n,int*P,int*B,int cur) 2 { 3 if(cur==n) 4 { 5 for(int i = 0;i<n;i++) 6 { 7 if(B[i]) cout << P[i] << " "; 8 } 9 cout << endl; 10 return; 11 } 12 B[cur] = 1; 13 print_subset_4(n,P,B,cur+1); 14 B[cur] = 0; 15 print_subset_4(n,P,B,cur+1); 16 }
3 二進制法
思想上與位向量法近似。
代碼:
1 void print_subset(int n,int s) 2 { 3 for(int i = 0;i<n;i++) 4 { 5 //cout << s << " " << i << " " << (s&(1<<i)) << endl; 6 if(s&(1<<i)) cout << i << " "; 7 } 8 cout << endl; 9 } 10 void print_subset_5(int n) 11 { 12 for(int i = 0;i<(1<<n);i++) 13 { 14 //cout << i << endl; 15 print_subset(n,i); 16 } 17 }
可能理解起來有點繞,主要是用十進制的二進制表示和十進制的數值本身有點混
舉個栗子:
現在n=4
那么在print_permutation_5里面我有循環了
0-15,二進制位從0-1111;它就相當於位向量,提供子集中元素出現的位置
接着對於每一個上面的數來依次與0~3做位移運算后得到的1,2,4,8,(二進制為0001,0010,0100,1000),來按位與,實際上是看0-3這個數在子集中是否出現
由於0-15已經含了對於元素個數為4的集合的所有子集的可能情況,那么與之后就得到該出現的子集元素。
代碼匯總:

1 #include <iostream> 2 #define max_n 10005 3 using namespace std; 4 int A[max_n]; 5 int B[max_n]; 6 //增量構造法 7 void print_subset(int n,int*A,int cur) 8 { 9 for(int i = 0;i<cur;i++) 10 { 11 cout << A[i] << " "; 12 } 13 cout << endl; 14 int s = cur? A[cur-1]+1 : 0;//確定當前元素最小可能值 15 for(int i = s;i<n;i++) 16 { 17 A[cur] = i; 18 print_subset(n,A,cur+1); 19 } 20 } 21 void print_subset_2(int n,int*P,int*A,int cur) 22 { 23 for(int i = 0;i<cur;i++) 24 { 25 cout << P[A[i]] << " "; 26 } 27 cout << endl; 28 int s = cur?A[cur-1]+1:0; 29 for(int i = s;i<n;i++) 30 { 31 A[cur] = i; 32 print_subset_2(n,P,A,cur+1); 33 } 34 } 35 //位向量法 36 void print_subset_3(int n,int*B,int cur) 37 { 38 if(cur==n) 39 { 40 for(int i = 0;i<n;i++) 41 { 42 if(B[i]) cout << i << " "; 43 } 44 cout << endl; 45 return; 46 } 47 B[cur] = 1; 48 print_subset_3(n,B,cur+1); 49 B[cur] = 0; 50 print_subset_3(n,B,cur+1); 51 } 52 void print_subset_4(int n,int*P,int*B,int cur) 53 { 54 if(cur==n) 55 { 56 for(int i = 0;i<n;i++) 57 { 58 if(B[i]) cout << P[i] << " "; 59 } 60 cout << endl; 61 return; 62 } 63 B[cur] = 1; 64 print_subset_4(n,P,B,cur+1); 65 B[cur] = 0; 66 print_subset_4(n,P,B,cur+1); 67 } 68 //二進制法 69 void print_subset(int n,int s) 70 { 71 for(int i = 0;i<n;i++) 72 { 73 //cout << s << " " << i << " " << (s&(1<<i)) << endl; 74 if(s&(1<<i)) cout << i << " "; 75 } 76 cout << endl; 77 } 78 void print_subset_5(int n) 79 { 80 for(int i = 0;i<(1<<n);i++) 81 { 82 //cout << i << endl; 83 print_subset(n,i); 84 } 85 } 86 int main() 87 { 88 int P[] = {1,2,3,4}; 89 //print_subset_2(4,P,A,0); 90 //print_subset_3(4,B,0); 91 //print_subset_4(4,P,B,0); 92 print_subset_5(4); 93 return 0; 94 }