zoukankan      html  css  js  c++  java
  • 集合的整数表示与子集枚举

    集合{0,1,…,n-1}的子集S可以编码成整数:f(S)=∑2i,像这样表示之后,一些集合运算可以对应地写成如下方式:

    ①空集Ø:……………………………………………0

    ②只含有1个元素i的集合{i}…………………………1<<i

    ③含有全部n个元素的集合…………………………(1<<n)-1

    ④判断第i+1个元素i是否属于集合S………………if (S>>i&1)

    ⑤向集合中加入第i+1个元素i(S∪{i})…………S|1<<i

    ⑥从集合中取出第i+1个元素(S{i})……………S&~(1<<i)

    ⑦集合S和集合T的并集S∪T………………………S|T

    ⑧集合S和集合T的交集S∩T………………………S&T
    此外,想要将集合{0,1,…,n-1}的所有自己枚举出来的话,可以这样写:

    for (int S=0; S<1<<n; S++)
    {
        //对子集的处理
    }

    接下来,介绍如何枚举某个集合sup的子集。这里sup是一个二进制码。

    (sub-1) & sup

    (sub-1)&sup会忽略sup中的0而从sub中减去"1",最终sub可以将sup所有的子集按照降序生成出来。为什么不能用加?因为(sub+1)&sup虽然是sup的子集,但很可能依旧是sub,没有任何改变,而用减就不会出现这样的情况。

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

    最后,介绍如何枚举{0,1,…,n-1}所包含的所有大小为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之后一个二进制码了,方法如下:
    ①求出最低位的1开始的连续的1的区间(0101110→0001110)
    ②将这一区间全部变为0,并将区间左侧的那个0变为1(0101110→0110000)
    ③将第①步里取出的区间右移,直到剩下的1的个数减少了1个(0001110→0000011)
    ④将第②步和第③步的结果按位取或(0110000|0000011→0110011)
    对于非零的整数,x&(-x)的值就是将其最低位的1独立出来的值,即lowbit

    除上述例子外,还可以利用位运算完成满足其它条件的集合的枚举,例如不包含相邻元素的集合等

  • 相关阅读:
    leetcode 13. Roman to Integer
    python 判断是否为有效域名
    leetcode 169. Majority Element
    leetcode 733. Flood Fill
    最大信息系数——检测变量之间非线性相关性
    leetcode 453. Minimum Moves to Equal Array Elements
    leetcode 492. Construct the Rectangle
    leetcode 598. Range Addition II
    leetcode 349. Intersection of Two Arrays
    leetcode 171. Excel Sheet Column Number
  • 原文地址:https://www.cnblogs.com/Ymir-TaoMee/p/9509521.html
Copyright © 2011-2022 走看看