摘要:在备赛刷题的过程中,同学们有没有发现部分问题可以转化为01的位运算问题,但是自己却始终背不下处理二进制状态的代码?或者自己也不太会设计二进制状态变化的运算呢?
在备赛刷题的过程中,同学们有没有发现部分问题可以转化为01的位运算问题,但是自己却始终背不下处理二进制状态的代码?或者自己也不太会设计二进制状态变化的运算呢?
其实,在C++的STL标准模版库中,有一个处理01数据非常便利的容器——bitset,可以方便管理每一个bit位,而不用自己来设计位运算。
01bitset介绍
bitset的中文名称是位图,包含在头文件#include中。这个特殊的容器中只能存放0和1,且每个元素只占1个比特(1 bit)。
①bitset a;
尖括号里是数组的元素个数,a是数组名称。这一句代码创建了一个位图数组a,由于没有声明初始值,8个元素都初始化为0。
②bitset b(12);
定义了长度为8的位图数组b,其中将12转换为二进制数保存,不足8位则高位(即数组的前面)补全;
b数组为{0,0,0,0,1,1,0,0};
特别地,如果在将小括号中的数值转化为二进制时,位数超过了数组大小,那么只会存储二进制表示的前几位。
③bitset c("10101010")
将字符串转化为01串存储进位图数组,同样字符串长度不足高位补全,但如果字符串长度超出了数组大小,只保留字符串的前几位。
(2)容器元素的访问
可以直接输出数组名,输出的结果是整个数组的内容;也可以想数组一样用下标访问特定元素。例如:
bitset a(500);cout输出的结果:
从结果中我们发现,从a[0]开始,数组是从低位开始存储二进制每一位的值。
02
bitset中的常用方法
bitset内置了很多方法函数,其中有一些函数对于二进制状态压缩技巧而言非常有用,我们一起来学习吧!!
以“bitset a("10101010")”为例:
(1)a.count;返回bitset中“1”的个数,这里对a数组计算的返回值是4;
(2)a.any:判断bitset中是否有“1”。这里对a数组计算的返回值是true;
(3)a.none:判断bitset是否都是“0”。这里对a数组计算的返回值是false;
(4)a.all:判断bitset是否都是“1”。这里对a数组计算的返回值是false;
(5)a.set:将bitset的全部位置设为“1”。这里对a数组计算后,数组变成“11111111”;
(6)a.set(2,1):将bitset指定位置设为“0”或“1”。这里对a数组计算后,数组变成“10101110”;
(7)a.reset:将bitset全部位置设为“0”,括号里也可以填写参数,表示将指定位置设为“0”。这里对s数组计算后,数组变成“00000000”;
(8)a.flip:将bitset全部位置取反。这里对a数组计算后,数组变成“01010101”;
(9)x=a.to_string:将数组全部转换为字符串。这里代码运行后,x被赋“10101010”字符串的值。
(10)x=a.to_ulong:将数组全部转换为unsigned long无符号长整型。这里代码运行后,x的值为170。
x=a.to_ullong:将数组全部转换为unsigned long long无符号长长整型。
03
例题应用
例题1:洛谷P1100 高低位交换
算法解析
(1)本题最简单的做法是充分运用位运算的左移和右移运算符,定义一个无符号整型unsigned int a,刚好能表示一个32位的二进制数,从而设计出表达式:
(a>>16) | (a
实现对题意的模拟。
(2)用本期文章的bitset来做做看吧!
在输入完数字后,定义两个bitset位图数组bit和rev_bit,bit数组初始化为输入的数值,再根据题意将bit数组的前16位和后16位分别赋值给rev_bit数组的后16位和前16位,最终转换为数字后输出。示例代码如下:
unsigned long a;cin>>a;bitset bit(a),rev_bit(0);//rev_bit的数组名含义取自"翻转"的英文reverse for(int i=0;i例题2:[NOIP1996 提高组] 砝码称重
算法解析
(1)本题的本质是一个多重背包问题,需要用到动态规划的相关知识;但是由于数据较少,使用6重循环直接暴力枚举也能通过本题;
(2)看看bitset对这道题的作用
这里我们要充分发挥bitset占内存小以及能进行位运算的特点,先考虑bitset每位的含义。
设bitset的每一位表示当前位对应的重量可以被表示出来,例如:“01110011”表示0、1、4、5、6g的重量可以被表示,“11000000 00000011”表示0、1、14、15g的重量可以被表示。
再根据题意想到本题的递推关系:如果重量dp[i]能被表示,那么dp[i+w[i]]也一定能被表示。因此,我们可以这样操作:
假设一开始能表示“000111”,即3种重量,那么再加上一个w[i]=2的砝码后,每个能被表示的重量+2也能被表示出来,即“011100”能被表示,再将加上砝码前后的两组方案数都组合起来,变成“011111”,就成功更新出新的方案数了。
这里聪明的同学已经注意到了,加上一个砝码,在代码中对应着是位左移运算,方案数合并,对应的是按位或运算。因此假设定义bitset a;每次添加一个砝码w[i],那么更新方案数的代码为a=a|(a
在将全部砝码都枚举完毕后,只需要用a.count计算出bitset中有多少个“1”,就表明能表示出多少种重量了。
示例代码:
#include#includeusing namespace std;int w[6]={1,2,3,5,10,20};bitset ans(1);//能表示的最大重量为1000,习惯性数组开大一点;//首位的1表示0g能被表示,最后要减去这个情况 int main{ int a[6]; for(int i=0;i>a[i]; } for(int i=0;i来源:香寒教育