zoukankan      html  css  js  c++  java
  • [数字技巧]子集问题(寻找给定集合的所有子集)

      我们定义该问题如下:

      给定一个集合C,找出所有的集合C',使得C'包含于C。

    一、无重复元素的集合

      我们首先来考虑一种简单的情形,C中的数都是各不相同的,这就意味着所产生的子集不会有重复的。

      直观来说,求一个集合的子集,无非就是对每个元素进行枚举,枚举两种状态”选“还是”不选“。例如,对一个集合C,当对cur这个位置的元素进行枚举时,对剩余的元素可以递归调用这个枚举的过程,当cur为数组长度n时,代表枚举结束。

      子集的解空间又叫子集树,其叶子节点所代表的状态即为所有的子集。例如,集合{1,3,5},其子集树如下所示:

      如上图所示,共有8个叶子节点,代表8个子集,有了子集树,要求出所有的子集,显然就是一个二叉树的先序遍历问题了,这里我们要设置一个与原数组一样大的标志数组,来标志当前位置”选“还是”不选“,因此,这种方法又叫”位向量法“,代码如下:

     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("
    ");
    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 }
    View Code

      另一种有趣的构造子集的方法叫”增量构造法“,顾名思义,这种做法是一种增量式的做法,例如,对集合{1,3,5},首先确定第一个元素为1,这就是一个子集,然后再这个基础上决定是不是增加3,或者5,形成另外两个子集{1,3},{1,5},{1,3}再可以决定是不是要继续增加5,而{1,5}中5已经到了最后,因此不能再添加,同样,可以确定第一个元素为2,重复上述步骤。注意,这里集合是定序的,以防止重复。下图给出了使用”增量构造法“对{1,3,5}进行子集搜索的子集树。

      

      下面我们给出使用”增量构造法“的子集生成代码:

     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("
    ");
    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 }
    View Code

      增量构造法可以很容易构造出指定大小的子集,只需要控制cur的大小指定输出即可,比如要输出长度不超过的3的子集,只要在程序一开始加上 if(cur>3) return; ,要输出长度为3的,只需要在for循环外加一个if就行了。

    练习题:

      1、http://www.cnblogs.com/codershell/p/3619928.html

    二、含有重复元素的集合

      如果原数组中存在重复的元素,那么用上述方法在子集生成时就会产生重复的子集,这里我是采用最简单的查重方法。代码如下:

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

      不知道有没有什么巧妙的方法来解决这个问题。

    练习题:

      http://www.cnblogs.com/codershell/p/3621687.html

  • 相关阅读:
    DOM对象和JQuery对象的区别
    处理android手机html5页面中,点击text文本框无法自动获取焦点的处理方法
    ORACLE之VBO-5530无法删除用户的解决办法
    当oracle clob类型不能和group by并用,但是需要去除多列重复
    Android 4主线程访问网络
    Android: How to get Address from geolocation using Geocoder
    解决乱码的最后方法
    tomcat启动时自动运行代码
    android 组件隐蔽显示状态
    android模拟器Genymotion 连接eclipse项目
  • 原文地址:https://www.cnblogs.com/codershell/p/3619310.html
Copyright © 2011-2022 走看看