bitset用法及例题

参考:

https://blog.csdn.net/snowy_smile/article/details/79120063

1.bitset有什么用?

优化代码运行的时间和空间
具体地,空间为 S/8 , 时间为 T/w (w通常为32,与计算机的机器字长有关)

2.bitset是什么

可以理解成N位(自己可设置)二进制位
和bool数组的区别:bool类型变量占用1字节内存,而每个bitset占用1bit内存

测试代码:

#include<bits/stdc++.h>
using namespace std;

int main(){
    bool a[32];
    cout<<sizeof(a)<<endl;
    
    bitset<32>b;
    cout<<sizeof(b)<<endl;

    system("pause");
    return 0;
}

3.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]取反

4.例题

例题1. 共有n个数,每个数的取值范围是[li,ri],S=xi2,求S的不同种类数
https://ac.nowcoder.com/acm/problem/17193

很容易想到dp方程:

//首先容易发现S的取值范围为1e6,用dp[i]表示i能否被取到
for i in [1,n]
  for k in [li,ri]
    for j in [1,1e6]
      dp[j]=dp[j]|dp[j-k*k]

然而时间复杂度太大

正解:
用大小为1e6的bitset表示每一个数能否取到(例如,第3位为1表示能取到3,第1位为0表示不能取到1)

dp[0][0]=1;
for(int i=1;i<=n;i++){
    cin>>l[i]>>r[i];
    for(int j=l[i];j<=r[i];j++){
        dp[i]|=(dp[i-1]<<(j*j));    //dp[i]表示前i个数的平方和的取值情况,每一位取值1表示能,0表示不能。如果某一位在dp[i]已经能取到1,或者前i-1个数不能,但是加上j*j就可以,那么这一位的值为1
    }
}
printf("%d\n",dp[n].count());

例题2.给你n个数,每次可以从中拿走1-3个数,问你有没有可能在留下的数中选取恰好10个数,使得他们的和为87
https://acm.hdu.edu.cn/showproblem.php?pid=5890

本题的做法是开87位的bitset,存储每个数(1~87)是否能被取到。
细节见代码
另外还需要优化时间复杂度,开一个数组记录每次拿走的数即可

int a[55];
bool vis[55];
bitset<100>dp[11];
int ans[55][55][55];

bool solve(){  //任意选10个数能否凑出87
    for(int i=0;i<=10;i++) dp[i].reset();
    dp[0][0]=1;
    for(int i=1;i<=n;i++){
        if(!vis[i]||a[i]>87) continue;
        for(int j=10;j>=1;j--){
            dp[j]|=(dp[j-1]<<a[i]);
        }
    }
    if(dp[10][87]) return 1;
    else return 0;
}

int main(){
    scanf("%d",&t);
    while(t--){
        memset(ans,-1,sizeof(ans));
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]),vis[i]=1;
        scanf("%d",&m);
        for(int i=1,x,y,z;i<=m;i++){
            scanf("%d%d%d",&x,&y,&z);
            if(x>y) swap(x,y);
            if(y>z) swap(y,z);
            if(x>y) swap(x,y);
            vis[x]=vis[y]=vis[z]=0;
            if(ans[x][y][z]==-1) ans[x][y][z]=solve();
            if(ans[x][y][z]) puts("Yes");
            else puts("No");
            vis[x]=vis[y]=vis[z]=1;
        }
    }
    system("pause");
    return 0;
}

例题3.bitset优化背包问题:给你n件物品,有各自的重量和价值。问你在总重量一定的情况下,所有物品价值的异或和最大能是多少。
本题没有提交链接

分析:
首先明确一个事实,价值的异或和是必须记录在状态中的,因为不一定从上一个最优状态能转移到下一个最优状态。(例如父状态选取物品1最优,子状态选取物品2+4最优。)
暴力做法: dp[W][V]=max(__,dp[W-w[i][V^v[i]]); 时间复杂度O(nWV) tle!
优化:开V个大小为W的bitset,存储异或和为V时的重量,这样做。

#include<bits/stdc++.h>
using namespace std;
const int N = 1024;
int t,n,m;
int w[N],v[N];
bitset<N>dp[N],tem[N]; 

int main(){
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%d%d",&w[i],&v[i]);
        }

        for(int i=0;i<1024;i++) dp[i].reset();
        dp[0][0]=1;
        for(int i=1;i<=n;i++){
            // for(int j=;;j){   ///?
            //     dp[j^v[i]]|=(dp[j]<<w[i]);   //dp[j]不一定是做过的
            // }
            for(int j=0;j<1024;j++){
                tem[j^v[i]]=(dp[j]<<w[i]);
            }
            for(int j=0;j<1024;j++){
                dp[j]|=(tem[j]);
            }
            for(int j=0;j<1024;j++){
                tem[j]=0;
            }
        }
        
        int ans=-1;
        for(int i=1023;i>=0;i--){
            if(dp[i][m]){
                ans=i;
                break;
            }
        }
        printf("%d\n",ans);
    }
    system("pause");
    return 0;
}
posted @   starlightlmy  阅读(356)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示