信息学奥赛编程-bitset让你不用那么费心去思考位运算该如何设计

摘要:在备赛刷题的过程中,同学们有没有发现部分问题可以转化为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

来源:香寒教育

相关推荐