子集生成的三種算法


子集生成算法:

給定一個集合,枚舉所有可能的子集。暫時討論沒有重復元素的情況。

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 }
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM