码迷,mamicode.com
首页 > 其他好文 > 详细

子集生成——暴力求解,枚举

时间:2015-02-26 14:59:40      阅读:221      评论:0      收藏:0      [点我收藏+]

标签:uva   算法   子集生成   枚举   暴力求解   

子集生成:给定一个集合,枚举它所有可能的子集。(简单起见,这里假设集合中没有重复元素)

一、增量构造法

思路:一次选出一个元素放到集合中。

Code:

void print_subset1(int n, int *A, int cur)
{//增量构造法 
  for(int i=0;i<cur;++i)
    printf("%d ",A[i]);
  printf("\n");
  
  int s=cur ? A[cur-1]+1 :0;//确定当前元素的最小可能值
  for(int i=s;i<n;++i)
  {
    A[cur]=i;
    print_subset1(n,A,cur+1);//递归构造子集      
  }   
} 
其中函数开始部分三行是用于输出一个子集的。s 变量用于确定当前cur位置元素的最小可能值。
上面的代码用到了定序的技巧,规定集合中所有元素的编号从小到大排列,就不会把集合{1,2}安装{1,2}和{2,1}输出两次了。

解答树对应2的n次方个结点。每个可能的A都对应一个结点,而n个元素恰好有2的n次方个子集。相比于方法2,即不存在部分解。

练习:UVa 11205 毁坏的步数计

二、位向量法

思路:构造一个位向量B[i],而不是直接构造子集A[i]本身,B[i]标记元素 i 在子集A中。

Code:

void print_subset2(int n, int *B, int cur)
{//位向量法 
  if(cur==n)
  {//只有当"所有元素是否选择"全部确定后,才是一个完整的子集 
    for(int i=0;i<cur;++i)
     if(B[i]) printf("%d ",A2[i]);
    printf("\n");
    return ;        
  } 
    
  B[cur]=1;//第cur个元素选取 
  print_subset2(n,B,cur+1);
  B[cur]=0;//第cur个元素不选取
  print_subset2(n,B,cur+1); 
}
其中函数前半部分的 if 语句也是用于输出一个子集。后面代码用于确定选择或不选择第cur个元素。

只有当cur==n时,即所有元素是否选择都确定后,才是一个完整的子集,才输出。

解答树上有2的n+1次方-1个结点。即1+2+4+8+...+2^n=2^(n+1)-1

三、二进制法

思路:从右往左第 i 位表示元素 i 是否在子集中。二进制110表示子集{2,1}。(各位从0开始编号,即最右是0号位)

异或运算的开关性:异或两次以后相当于没有异或,即A^B^B=A.

A&B、A|B、A^B 对应集合的交、并和对称差。空集为0,全集{0,1,2,...,n-1}的二进制为全1,即十进制的2^n-1。往往在程序中把全集定义为ALL_BITS=(1<<n)-1,则A的补集为ALL_BITS^A。

Code:

void print_subset3(int n,int s)
{//输出子集对应的各个元素
  for(int i=0;i<n;++i)
    if(s&(1<<i)) printf("%d ",i);//1对应输出0,2对应1,3对应01,4对应2
  printf("\n");  
}
//枚举子集
for(int i=0;i< (1<<n);++i)
  print_subset3(n,i); 

可以看到,三种方法的输出都不是按照子集元素个数升序输出的,没有固定的规律。

下面是可以直接运行的程序,其中前两种方法从1开始编号,第三种方法从0开始编号,生成子集。

#include<stdio.h>
#include<stdlib.h>

void print_subset1(int n, int *A, int cur);
void print_subset2(int n, int *B, int cur);
void print_subset3(int n,int s);

int A[10];
int A2[]={1,2,3,4,5,6,7,8,9,10};
int B[10];

int main()
{
  /*printf("增量构造法:\n");
  print_subset1(10,A,0);
  system("pause");
  printf("位向量法:\n");
  print_subset2(10,B,0);
  system("pause");*/
  printf("二进制法:\n");
  int n=10;
  for(int i=0;i<(1<<n);++i){
    print_subset3(n,i);
  system("pause");}
  return 0;
}

void print_subset1(int n, int *A, int cur)
{//增量构造法 
  for(int i=0;i<cur;++i)
    printf("%d ",A[i]);
  printf("\n");
  
  int s=cur ? A[cur-1]+1 :1;//确定当前元素的最小可能值
  for(int i=s;i<n+1;++i)
  {
    A[cur]=i;
    print_subset1(n,A,cur+1);//递归构造子集      
  }   
} 

void print_subset2(int n, int *B, int cur)
{//位向量法 
  if(cur==n)
  {//只有当"所有元素是否选择"全部确定后,才是一个完整的子集 
    for(int i=0;i<cur;++i)
     if(B[i]) printf("%d ",A2[i]);
    printf("\n");
    return ;        
  } 
    
  B[cur]=1;//第cur个元素选取 
  print_subset2(n,B,cur+1);
  B[cur]=0;//第cur个元素不选取
  print_subset2(n,B,cur+1); 
}

void print_subset3(int n,int s)
{//输出子集对应的各个元素
  for(int i=0;i<n;++i)
    if(s&(1<<i)) printf("%d ",i);
  printf("\n");  
}

参考:刘汝佳《算法竞赛入门经典》


子集生成——暴力求解,枚举

标签:uva   算法   子集生成   枚举   暴力求解   

原文地址:http://blog.csdn.net/buxizhizhou530/article/details/43952075

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!