1. 程式人生 > 其它 >淺析 c++ bitset 的用法

淺析 c++ bitset 的用法

淺析 c++ bitset 的用法

總述

C++的 \(bitset\) 位於 <bitset> 標頭檔案中,這是一種類似於陣列的資料結構,每個位置儲存 \(0\ or\ 1\) ,並且每個元素僅用 \(1\ bit\) 的空間

如果換一種方式來想,\(bitset\) 就是一個封裝了一堆奇奇怪怪操作並支援狀態壓縮\(bool\) 陣列,而且支援基本的位運算

定義 or 宣告

bitset<4> bitset1;  //無參構造,長度為4,預設每一位為0
	
bitset<8> bitset2(12);  //長度為8,二進位制儲存,前面用0補充

/*用string物件初始化bitset*/
string s = "100101";
bitset<10> bitset3(s);  //長度為10,前面用0補充
    
/*用char物件初始化bitset*/
char s2[] = "10101";
bitset<13> bitset4(s2);  //長度為13,前面用0補充

bitset<2> bitset5(12) //12的二進位制為1100(長度為4),但bitset1的size=2,只取後面部分,即00

cout << bitset1 << endl;	//0000
cout << bitset2 << endl;	//00001100
cout << bitset3 << endl;	//0000100101
cout << bitset4 << endl;	//0000000010101
cout << bitset5 << endl;	//00

需要注意的是:在用string去初始化的時候,string 中的字元只能為 \(0\ or\ 1\)

操作

1.運算

位操作符 的用法相同

bitset<4> foo (string("1001"));
bitset<4> bar (string("0011"));

cout << (foo^=bar) << endl;       // 1010 (foo對bar按位異或後賦值給foo)
cout << (foo&=bar) << endl;       // 0010 (按位與後賦值給foo)
cout << (foo|=bar) << endl;       // 0011 (按位或後賦值給foo)

cout << (foo<<=2) << endl;        // 1100 (左移2位,低位補0,有自身賦值)
cout << (foo>>=1) << endl;        // 0110 (右移1位,高位補0,有自身賦值)

cout << (~bar) << endl;           // 1100 (按位取反)
cout << (bar<<1) << endl;         // 0110 (左移,不賦值)
cout << (bar>>1) << endl;         // 0001 (右移,不賦值)

cout << (foo==bar) << endl;       // false (0110==0011為false)
cout << (foo!=bar) << endl;       // true  (0110!=0011為true)

cout << (foo&bar) << endl;        // 0010 (按位與,不賦值)
cout << (foo|bar) << endl;        // 0111 (按位或,不賦值)
cout << (foo^bar) << endl;        // 0101 (按位異或,不賦值)

2.訪問

可以通過訪問陣列下標的形式訪問 \(bitset\) 中的元素,注意最低位下標為 \(0\)

同時,也可以通過這種方式進行單點修改

bitset<4> foo ("1011");
    
cout << foo[0] << endl;	//1
cout << foo[1] << endl;	//1
cout << foo[2] << endl;	//0
cout << foo[3] << endl;	//1

3.一些函式的使用

bitset<1000> s;

s.count();	//返回s中1的個數

s.any();	//當s全為0時,返回false;如果有任何一位為1,則返回true
s.none();	//當s全為0時,返回true;如果有任何一位為1,則返回false

s.set();	//將s中每一位都設定為1
s.set(3,0);	//將s中第3位的數值設定為0
s.set(3);	//將s中第3位的數值設定為1

s.reset();	//將s中每一位都設定為0
s.flip();	//對s中每一位都取反

需要注意的是 s.reset()s.flip() 也可以傳引數,和 s.set 的用法大致相同

4.一些操作

對於一類題,有這樣的書寫方式

s |= s << w[i]

這句程式碼實際上是將 \(s\) 左移了 \(w[\ i\ ]\) 位,並且與原來的 \(s\) 取並集

下面拿兩道例題舉舉栗子

Luogu P2347 [NOIP1996 提高組] 砝碼稱重

#include <bits/stdc++.h>
using namespace std;
int w[10]={0,1,2,3,5,10,20},a[10];
bitset<1010> s;

int main(){
	for(int i=1;i<=6;i++)
		cin>>a[i];
	s[0]=1;
	for(int i=1;i<=6;i++)
		for(int j=0;j<a[i];j++)
			s|=s<<w[i];
	cout<<"Total="<<s.count()-1<<endl;
	return 0;
}

Luogu P1441 砝碼稱重

#include <bits/stdc++.h>
using namespace std;
const int N=2010;
int a[50],n,m,ans;

inline int read(){
	int f=1,x;
	char ch;
	while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
	x=ch-'0';
	while('0'<=(ch=getchar())&&ch<='9') (x*=10)+=ch-'0';
	return x*f;
}

inline int cal(unsigned int x){
	int ret=0;
	while(x!=0){
		if(x&1) ret++;
		x>>=1;
	}
	return ret;
}

int main(){
	n=read(),m=read();
	for(int i=0;i<n;i++)
		//scanf("%d",&a);
		a[i]=read();
	for(int i=0;i<(1<<n);i++){
		if(cal(i)==n-m){
			bitset<N> s;
			s[0]=1;
			for(int j=0;j<n;j++)
				if(i&(1<<j))
					s|=s<<a[j];
			ans=max(ans,(int)s.count());
		}
	}
	cout<<ans-1<<endl;	
	return 0;
}

這兩個題在對可以稱出的質量進行統計的時候使用了這個小技巧,就可以擺脫 \(dfs+dp\) 的複雜方式,從而轉化為 \(bitset\) 的一道題目,大大優化了時間複雜度和空間複雜度