求自己总共有三种方式:
增量构造
位向量
二进制
首先假设集合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
但是编程的复杂度而言二进制最简单,增量构造和位向量一致
&符号代表所有的位一起与。
原文地址:https://www.cnblogs.com/TorettoRui/p/10466839.html
时间: 2024-11-04 12:51:27