zoukankan      html  css  js  c++  java
  • 子集生成的三种算法

    子集生成算法:

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

    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
  • 相关阅读:
    android 调试Installation failed with message INSTALL_FAILED_USER_RESTRICTED: Install canceled by user.
    selenium 调用方法
    正则去除空行
    tmux用法
    win10专业版激活
    11.17
    git reset,git checkout区别
    git reset revert区别
    python多线程,守护线程
    win7 32位安装 mong0db
  • 原文地址:https://www.cnblogs.com/zhanhonhao/p/11234673.html
Copyright © 2011-2022 走看看