集合的整数表示(二进制枚举)

集合的整数表示(二进制枚举)

原理

对于集合{0,1,2,3…,n-1}的子集:如{0}、{0,1}、{0,1,2 }…

  1. 总共有2n:因为C(n,0)+C(n,1)…+C(n,n)=2n,即1<<n个
  2. 可以用1<<n的二进制码中1与0枚举它们
    1. 1代表集合中有
    2. 0代表集合中没有

使用方法

运算规则表示形式
空集 Φ \Phi Φ0
只含有第i个元素的集合{i}1<<i
含有全部n个元素的集合{0,1,2…,n-1}(1<<n)-1
判断第i个元素是否属于集合Sif(S&(1<<i))
向集合中加入第i个元素S U {i}S|(1<<i)
从集合中删除第i个元素S U {i}S&~(1<<i)
集合S与集合T的交集S U TS&T
集合S与集合T的并集S ∩ \cap TS|T

子集枚举

将集合{0,1,2…,n-1}的子集都枚举出来

for(int S=0;S<1<<n;S++){//枚举所有子集
    //对子集的操作,例如输出子集元素
    for(int j=0;j<n;j++){//遍历子集二进制的每一位
        if(S&1<<j){//若过第j位存在
            cout<<j<<endl;//输出第j位
        }
    }
}

子集的子集的枚举

例如:枚举集合sup{01101100}的子集sub(它的子集如{01100000}、{00101100})

  1. 像子集枚举一样从0开始不断加1来枚举全部的子集,为了保证集合sub+1是集合sup的子集,就得取交集(sub+1)&sup

    但是这会出现(sub+1)&sup仍然是相同的子集

    ​ 如:sub={00101100}:sub&sup=sub、(sub+1)&sup=sub

  2. 所以我们可以从从sup开始每次减1直到0为止,为了保证集合sub-1是集合sup的子集,取交集(sub-1)&sup,这样的话可以将sup中所有子集按降序枚举出来,并且(sub-1)&sup会忽略sup中的0而从sub中减去1

int sub=sup;
do{
    //对子集的处理
    sub=(sub-1)&sup;
}while(sub!=sup);//处理完0之后,sub=-1,-1的补码全是1,会有-1&sup=sup

枚举{0,1,……,n}所包含的所有大小为k的子集

代码

按字典序升序枚举出所有满足条件的二进制码

int comb=(1<<k)-1;
while(comb<1<<n){
    //这里进行对组合的处理
    int x=comb&-comb,y=comb+x;
    comb=((comb&~y)/x>>1)|y;
}

如何实现字典序升序

按字典序的话最小的子集为(1<<k)-1,因此从它开始枚举

如何求出comb下一个二进制码

如0101110之后的是0110011

0111110之后的是1001111

  1. 求出最低位的1开始的连续的1的区间(0101110→0001110)
  2. 将这一区间全部变为0,并将区间左侧那个0变成1(0101110→0110000)
  3. 将第1步里取出的区间右移,直到剩下的1的个数减少了1个(0001110→0000011)
  4. 将第2步和第3步的结果按位取或(0110000|0000011=0110011)

分析程序

1.x=comb&-comb作用

相当于comb&(~comb+1)→取最低位的1

2.y=comb+x

实现第2步将comb最低位开始的1的连续的1的区间变为0并且区间左侧那个0变成1

3.z=comb&~y

实现第1步:得到最低位1开始的连续区间 仔细分析分析,我好难解释啊😭😭

4.d=z/x>>1

实现第3步:将z不断右移,直到最低为1,在移一位

5.comb=d|y

实现第4步

就得到了comb之后的下一个二进制位了,因为从n个元素的集合中进行选择,所以comb的值不能大于等于1<<n

如果那个地方出错了请大佬指出,而且可能出现很多错别字sorry~嘻嘻
(✿◡‿◡) ( •̀ ω •́ )y

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值