[数字技巧]子集问题(寻找给定集合的所有子集)
2014-03-24 19:50 庸男勿扰 阅读(2717) 评论(4) 编辑 收藏 举报我们定义该问题如下:
给定一个集合C,找出所有的集合C',使得C'包含于C。
一、无重复元素的集合
我们首先来考虑一种简单的情形,C中的数都是各不相同的,这就意味着所产生的子集不会有重复的。
直观来说,求一个集合的子集,无非就是对每个元素进行枚举,枚举两种状态”选“还是”不选“。例如,对一个集合C,当对cur这个位置的元素进行枚举时,对剩余的元素可以递归调用这个枚举的过程,当cur为数组长度n时,代表枚举结束。
子集的解空间又叫子集树,其叶子节点所代表的状态即为所有的子集。例如,集合{1,3,5},其子集树如下所示:
如上图所示,共有8个叶子节点,代表8个子集,有了子集树,要求出所有的子集,显然就是一个二叉树的先序遍历问题了,这里我们要设置一个与原数组一样大的标志数组,来标志当前位置”选“还是”不选“,因此,这种方法又叫”位向量法“,代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 int n; 5 int A[20]; 6 int B[20]; 7 8 void print_subset(int n, int* B, int cur) 9 { 10 if(cur == n) 11 { 12 for(int i = 0; i < cur; i++) 13 if(B[i]) printf("%d ", A[i]); // 打印当前集合 14 printf("\n"); 15 return; 16 } 17 B[cur] = 1; // 选第 cur 个元素 18 print_subset(n, B, cur+1); 19 B[cur] = 0; // 不选第 cur 个元素 20 print_subset(n, B, cur+1); 21 } 22 23 int main() 24 { 25 scanf("%d",&n); 26 for(int i=0; i<n; i++) 27 scanf("%d",&A[i]); 28 29 print_subset(n,B,0); 30 system("Pause"); 31 32 return 0; 33 }
另一种有趣的构造子集的方法叫”增量构造法“,顾名思义,这种做法是一种增量式的做法,例如,对集合{1,3,5},首先确定第一个元素为1,这就是一个子集,然后再这个基础上决定是不是增加3,或者5,形成另外两个子集{1,3},{1,5},{1,3}再可以决定是不是要继续增加5,而{1,5}中5已经到了最后,因此不能再添加,同样,可以确定第一个元素为2,重复上述步骤。注意,这里集合是定序的,以防止重复。下图给出了使用”增量构造法“对{1,3,5}进行子集搜索的子集树。
下面我们给出使用”增量构造法“的子集生成代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 //增量构造法 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 6 int n; 7 int A[20]; 8 int B[20]; 9 10 void print_subset(int n, int* A, int cur); 11 12 int main() 13 { 14 scanf("%d",&n); 15 for(int i=0; i<n; i++) 16 scanf("%d",&A[i]); 17 18 print_subset(n,B,0); 19 system("Pause"); 20 21 return 0; 22 } 23 24 void print_subset(int n, int* B, int cur) 25 { 26 int i; 27 for(i= 0; i < cur; i++) printf("%d ",A[B[i]]); 28 printf("\n"); 29 int s = cur ? B[cur-1]+1 : 0;//确定当前元素的最小可能位置 30 for(i = s; i < n; i++) 31 { 32 B[cur] = i; 33 print_subset(n, B, cur+1);//递归构造子集 34 } 35 }
增量构造法可以很容易构造出指定大小的子集,只需要控制cur的大小指定输出即可,比如要输出长度不超过的3的子集,只要在程序一开始加上 if(cur>3) return; ,要输出长度为3的,只需要在for循环外加一个if就行了。
练习题:
1、http://www.cnblogs.com/codershell/p/3619928.html
二、含有重复元素的集合
如果原数组中存在重复的元素,那么用上述方法在子集生成时就会产生重复的子集,这里我是采用最简单的查重方法。代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 bool isExist(vector<vector<int> > &vv,vector<int> v){ 2 for(int i=0; i<vv.size(); i++) 3 if(vv[i] == v) 4 return true; 5 6 return false; 7 }
不知道有没有什么巧妙的方法来解决这个问题。
练习题:
http://www.cnblogs.com/codershell/p/3621687.html
出处:http://www.cnblogs.com/codershell
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
如果您觉得对您有帮助,不要忘了推荐一下哦~