枚举子集的几种方法

程序设计挑战竞赛上156页说了枚举组合和子集的几种方法,我觉得挺好的,收藏一下

都是利用二进制数的模型来进行枚举子集或者组合

下面枚举集合都是在二进制位上进行枚举。

##枚举k个数的子集

void EunmSet(int k)//用k个二进制位数枚举k个状态
{
    for(int i=0;i<1<<k;++i)
    {
        // 对子集的处理
    }
}

当k等于3时,即EunmSet(3)

输出:

000
001
010
011
100
101
110
111

枚举一个二进制状态的子集(其实我更喜欢for循环枚举)

void EnumSubSet(int sup)/*作用:对于二进制状态sup,枚举该状态的子集*/
{
    int sub=sup;
    do{
        /*对子集的处理*/
        sub=(sub-1)&sup;
    }
    while(sub!=sup);/*当sub=0之后 sub=0-1=-1,退出*/
}
    /*
    原理:针对sup中的二进制为1的位开始进行减法,假设有k个二进制位,那么像枚举(2^k-1)~0一样枚举其子集
    输出:
        状态为降序输出
    */

当sup=10101,即十进制数21

输出:

10101
10100
10001
10000
00101
00100
00001
00000

枚举n个状态中,k个状态成立的所有状态

void EnumK(int k,int n)/*求出总共n个状态中,有k个状态为1的所有情况*/
{
    int comb=(1<<k)-1;
    while(comb<1<<n)//comb>=2^n退出
    {
        //针对组合的处理
        int x=comb&-comb,y=comb+x;
        comb=((comb&~y)/x>>1)|y;
    }
}
    /*原理:
        根据当前的符合要求的状态求出第一个大于该状态的符合要求的状态
    输出:
        升序输出
     算法描述:
     按照字典序的话,最小的子集是(1<<k)-1,所以用它作为初始值。现在哦我们求出comb其后的二进制码
     1.求出最低位的1开始连续的1的区间  (x&(-x)的值就是将最低位的1独立出来的值)
     2.将这一区间全部变为0,并将区间最左侧的0变为1
     3.将第1步取出的区间右移,知道剩下的1的个数少了1个
     4.将第2步和第3步的结果按位取或
    */

输入:k=3,n=5

00111
01011
01101
01110
10011
10101
10110
11001
11010
11100

posted @ 2019-01-25 17:11  _年少有为  阅读(1957)  评论(0编辑  收藏  举报