子集生成的三种算法

子集生成算法:

给定一个集合,枚举所有可能的子集。暂时讨论没有重复元素的情况。

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

 

转载于:https://www.cnblogs.com/zhanhonhao/p/11234673.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值