Try Again

位运算与bitset

&运算

   将两个数转化为二进制后,对应的位置上相同即取,通常取1,所以&通常情况下可以用来枚举子集

  设x为表示集合的整数,那么这个整数有如下性质:

         x的子集整数y在数值上不会比x大。因为x的子集y只是保留了x某些位置上的1,所以y总可以加上一个非负的整数z等于x,相当于把没选的1补上。

         根据这个性质可知,可以通过枚举所有比x小的数p并判断,p是否只含x对应位上的1,如果是则p是x的子集,否则不是。这样时间复杂度是严格的x。有没有更快的呢,有的。

上诉枚举p是通过减一操作,并且我们知道减一操作一定是正确的,那么在枚举的时候如何快速的去掉多余的状态,答案就是和x进行&(与)运算。与运算可以快速跳到下一个子

集。

  &运算本质就是保留p在x对应位为1的数值,而根据二进制减法可知减一操作都是把p最低位的1消去,在那一位后全补上1,如果在x对应位为0的地方产生了1其实是无效的,

后续的减一操作也会把它消掉,所以直接&运算可以快速去掉多余的状态。时间复杂度是x的子集数。

for(int i=x;i;)
    i=(i-1)&x;

快速判断一个数是不是2的整数幂

bool fun(int n)
{
    return (!(n&(n-1))) && n;
}

改位

#define set_bit(x,ith,bool) ((bool)?((x)|(1<<(ith))):((x)&(~(1<<ith))))
//设置x的从第ith位开始连续k位位bol
int mset(int x,int ith,int k,int bol)
{
    while(k--) x=set_bit(x,ith+k,bol);
    return x;
}

|运算

两个相应的二进制位中只要有一个为1,该位的结果值为1。所以通常用来求并集

^运算

若参加运算的两个二进制位值相同则为0,否则为1,常用来取反

交换a,b;

void swap(int &a,int &b)
{
    a^=b,b^=a,a^=b;
}

 

~运算

一元运算符,用于求整数的二进制反码,即分别将操作数各二进制位上的1变为0,0变为1。

<<运算

左移运算符是用来将一个数的各二进制位左移若干位,移动的位数由右操作数指定(右操作数必须是非负 值),其右边空出的位用0填补,高位左移溢出则舍弃该高位。

>>运算

右移运算符是用来将一个数的各二进制位右移若干位,移动的位数由右操作数指定(右操作数必须是非负值),移到右端的低位被舍弃,对于无符号数,高位补0。对于有符号数某些机器将对左边空出的部分用符号位填补(即“算术移位”),而另一些机器则对左边空出的部分用0填补(即“逻辑移位”)。

bitset的用法

  bitset 是STL库中的二进制容器,根据C++ reference 的说法,bitset可以看作bool数组,但优化了空间复杂度和时间复杂度,并且可以像整形一样按位与或。

bitset申明长度

bitset<length>b;

赋值

bitset重载了[]运算符,可以像数组一样使用

b[0]=1;

bit的常用函数

  处理的数组只有0和1的变化,此时就可以使用bitset优化。比如求两个集合的交集可以使用按位与运算,求并集可以使用按位或运算

b.any() //b中是否存在置为1的二进制位?
b.none()// b中不存在置为1的二进制位吗?
b.count()//b中置为1的二进制位的个数
b.size() //b中二进制位数的个数
b[pos] //访问b中在pos处二进制位
b.test(pos)// b中在pos处的二进制位置为1么?
b.set() //把b中所有二进制位都置为1
b.set(pos) //把b中在pos处的二进制位置为1
b.reset() //把b中所有二进制位都置为0
b.reset(pos) //把b中在pos处的二进制位置置为0
b.flip() //把b中所有二进制位逐位取反
b.flip(pos) //把b中在pos处的二进制位取反
b.to_ulong() //把b中同样的二进制位返回一个unsigned

 

posted @ 2018-07-14 11:15  十年换你一句好久不见  阅读(923)  评论(0编辑  收藏  举报