zoukankan      html  css  js  c++  java
  • 子集生成——回溯法的准备篇

    在如何获得全排列的文字里,大家会发现递归可以说随处可见,而在回溯法中,递归更是实现枚举的基本手段。在生成子集的方法中,我们也将看到递归的影子

    为了简单,这里不涉及可重集的子集

    递归不能深究详细的过程,而要注意第一步向第二步如何推广,以及推广后的递归如何在边界终止。

    一、增量构造法

    这种思路和按照字典序枚举全排列的思路一致,本质是递归地构造子集。

    思路等同于解答树:

    1. 如果子集非空,则输出
    2. 递归调用,将a[cur-1]+1不断赋值给a[cur],然后递归找出剩余的子集
    3. 边界终止条件隐式的递归条件,当没有元素可加入时,递归就会停止

    代码如下:

     1 //定义数据
     2 int a[5];
     3 void print_subset(int n,int *a,int cur){
     4      for(int i=0;i<cur;i++)printf("%d%c",a[i],i<cur-1?' ':'
    ');//对于要求严格的题目,一定要保证输出没有多余的空格
     5      int s=cur?a[cur-1]+1:0;//定序技巧,避免子集重复
     6      for(int i=s;i<n;i++){
     7          a[cur]=i;//将新的元素加入子集的第cur个位置
     8          print_subset(n,a,cur+1);
     9      }
    10  }
    11 int main(){
    12     print_subset(5,a,0);
    13 }

    二、位向量法

    位向量法与直接构造法的区别在于数组的使用方式不同,位向量法用数组下标表示子集元素,而数组内容为1或0,表示该元素 在 或者 不在 集合内

    注意,和直接构造不同,每个元素都有0和1(不在/在)两种方式,因此,位向量法的解答树节点数比直接构造法多出一倍减一个,例如直接构造法的10个元素的解答树,有2^10=1024个节点,而用位向量法,因为每个元素都有0和1两种状态,因此有2*1024-1=2047个节点(根节点都只有一个,所以不是单纯2倍关系),因此速度理论上要慢一些,但多数情况下仍然够用

    位向量法需要指定显式的递归边界,因为我们不能明确知道究竟一次有多少元素会被打印。

     代码如下:

     1 int a[5];
     2 void print_subset(int n,int *a,int cur){
     3     if(cur==n){//显式的递归边界
     4         for(int i=0;i<n;i++)
     5             if(a[i])printf("%d ",i);
     6         printf("
    ");
     7         return;
     8     }
     9     a[cur]=1;//每个位置有0和1两种状态
    10     print_subset(n,a,cur+1);
    11     a[cur]=0;
    12     print_subset(n,a,cur+1);
    13 }

    三、二进制法(最快最简单代码最少,限制是数字会爆)

    本质和位向量法相同,但是由于是二进制,因此一个整数(1<<n)-1就可以代表一个全集,这种实现接近计算机底层,加上C语言自身就支持二进制的运算,如&(交集),|(并集),^(对称差和开关性)等,因此速度和代码简洁性都优于位向量法。

     二进制法从右向左表示集合中的元素(从低位到高位)如0110是数字6,代表的集合是{ 1,2 },010111是数字23,代表的是{ 0,1,2,4 },n个元素所有的可能的情况有2^n个,即(1<<n)-1种

    实现如下: 

     1 void print_subset(int n,int s){//s为{0,1,。。,n-1}的子集 
     2     for(int i=0;i<n;i++)
     3         if(s&(1<<i))printf("%d ",i);
     4     if(s)printf("
    ");
     5 }
     6 int main(){
     7     int n=5;
     8     for(int i=0,len=1<<n;i<len;i++)//枚举子集
     9         print_subset(n,i);
    10 }

    其实我觉得我对于二进制还不是很熟悉,因此二进制法应当继续加深练习。今天就到这,拜拜~~~

  • 相关阅读:
    用GDB调试程序(一)
    ZOJ Problem Set
    android 去除标题
    【hadoop之翊】——基于CentOS的hadoop2.4.0伪分布安装配置
    layoutSubviews总结
    用数据说话,外贸产品选择(中篇)-google趋势分析法
    Apache介绍
    浅谈android4.0开发之GridLayout布局
    Android GridView 分页加载数据
    Android TableLayout中的使用说明
  • 原文地址:https://www.cnblogs.com/luruiyuan/p/5625427.html
Copyright © 2011-2022 走看看