当前位置: 首页 > news >正文

巧用Bitset!优化dp

目录

  • 原理
    • 传统dp瓶颈
    • bitset优化思路
  • 经典例题:NC17193
    • 代码解释
      • 1. 初始 `dp` 的含义
      • 2. `dp << 4` 的含义
      • 3.`dp|=...`的含义
      • 4.dp.reset()和dp.count()的含义
  • 相关例题推荐
    • 小红组比赛

有关bitset的一些基本运用,请大家阅读这篇博客:
位图–Bitset【0基础详细版】
如果还想了解下位运算的话,可以看这篇博客
位运算【入门–>精通】

接下来我们要谈谈关于如何用bitset优化动态规划DP问题?

bitset是 C++ 标准库中的一个容器,用于高效存储和操作布尔值数组。它通过位运算将多个布尔状态压缩到一个或多个 32 位整数中,从而显著提升动态规划的时空效率。

bitset优化动态规划的核心原理在于利用位运算的高校性,将原本时间复杂度为O(n)转化为O(n/32)或O(n/64).

原理

传统dp瓶颈

下面以经典的01背包问题为例:

  • 状态:dp[j]表示能否组成j.
  • 转移:dp[j]=max(dp[j],dp[j-v]).
  • 时间复杂度:O(n*m)
  • 空间复杂度:O(m)
  • 缺点:当m很大时,循环次数多,且每个状态占1字节(8位),浪费空间!

bitset优化思路

状态压缩:用一位表示一个状态,空间能压缩8倍

  • 传统数组:每个状态占 1 字节(8 位),如bool dp[1000000]需 1MB 内存。
  • Bitset:每个状态占 1 位,bitset<1000000> dp仅需 125KB 内存。

位运算加速:一次位运算能处理32位,时间复杂度降为O(n*m/32)

  • 左移操作:dp << v 等价于将所有可达的和加上 v。
  • 按位或:dp |= … 将移动后的结果与原状态集合进行按位或操作,能快速合并多个状态集合。

什么?不太能看懂?模模糊糊?感觉很抽象?没关系没关系,初学时是很难理解,不过,有下面这个例子,相信你一定会豁然开朗!

经典例题:NC17193

简单瞎搞题
在这里插入图片描述
在这里插入图片描述
题目大意

给出n个区间,每次可以选区间中的任意一个数xi,然后求得S=∑xi2S = \sum x_{i}^{2}S=xi2,让你求这个s能有多少种?

思路

暴力枚举不可行:每个数有多个选择,直接枚举所有组合的时间复杂度是指数级的(最坏情况下每个区间长度为 100,5 个数就有 100^5 种组合)
动态规划思路:我们需要记录前 i 个数能组成的所有可能和,然后加入第 i+1 个数时更新这些和。但传统 DP 数组存储所有可能和会导致超时(时间复杂度 O (n * sum (ri)))
优化关键:利用 bitset 的位运算特性,将状态转移从 O (n) 优化到 O (n/32),同时压缩空间

AC代码展示

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+5;
int n,l,r;
bitset<N>dp,dt;
signed main()
{cin>>n;dp[0]=1;//表示1是可得的 while(n--){cin>>l>>r;dt.reset();//每有新的一组数,就要将初始置为0. for(int i=l;i<=r;i++){                  //或(|)运算,只要有一个1就为1;//不懂的后面会有详细的解释,一定要继续往下看。 dt|=dp<<(i*i); //相当于原来的和加上i*i(<<左移i*i,相当于加i*i) } dp=dt;//遍历完一个区间后要更新dp值 }//dp的第i为是1,表示i是可相加得到的和,pt中有多少1就相当于有几种和 cout<<dp.count();//输出dp中1的个数即S的种类数。return 0; 
}

代码解释

1. 初始 dp 的含义

假设 dp 是一个 10位的 bitset,每一位代表一个和是否可达:

Bit 位置: 9 8 7 6 5 4 3 2 1 0
dp 的值:  0 0 0 0 0 1 0 1 1 0
  • dp[0] = 0:和为 0 不可达
  • dp[1] = 0:和为 1 不可达
  • dp[2] = 1:和为 2 可达
  • dp[3] = 1:和为 3 可达
  • dp[4] = 0:和为 4 不可达
  • dp[5] = 1:和为 5 可达
  • dp[6] = 0:和为 6 不可达
  • dp[7] = 0:和为 7 不可达
  • dp[8] = 0:和为 8 不可达
  • dp[9] = 0:和为 9 不可达

所以 dp = 0000010110 表示和 {2,3,5} 可达。


2. dp << 4 的含义

dp << 4 表示将 dp 的所有位 左移 4 位,低位补 0:

原 dp:        0 0 0 0 1 0 1 1 0
左移 4 位:    1 0 1 1 0 0 0 0 0

所有可达的和 + 4

  • dp 表示和 {2,3,5} 可达。
  • 左移 4 位后:
    • 和 2 → 2 + 4 = 6(dp[6] = 1
    • 和 3 → 3 + 4 = 7(dp[7] = 1
    • 和 5 → 5 + 4 = 9(dp[9] = 1

所以 dp << 4 的结果应该是:

Bit 位置: 9 8 7 6 5 4 3 2 1 0
新 dp:    1 0 1 1 0 0 0 0 0 0

3.dp|=...的含义

dp |= …是指将移动后的结果原状态集合进行按位或操作
按位或( | ):只有都为0时才为0,只要有一个1就是1.
所以,就算用不同选择的方式相加得到的和相同,即不会重复计算也不会漏算,这一位为1就一直是1了。

4.dp.reset()和dp.count()的含义

dp.reset() 表示清空指定位或所有位,将其设为0
dp.count() 表示获取被设置为1的位的个数(即1的数量)
更多详细函数可参考一下:
在这里插入图片描述

看到这里你一定理解掌握了吧,这有一道热乎的同类型的题,让我们一起去练习吧~

相关例题推荐

小红组比赛

小红组比赛
在这里插入图片描述
在这里插入图片描述
思路

这道题和上一道很相似,我们可以先算出一共有多少种,然后再找出差值最小的那个输出即可。由题目给出的范围可知,最多有5000种可能的分数,所以直接遍历的话不会超时。

AC代码

#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define int long long
#define PII pair<int,int>
#define fi first
#define se second
#define endl '\n'
const int N=5055;
int n,m,x;
bitset<N>b,t; 
void solve()
{cin>>n>>m;b.set(0);//初始化为0 while(n--)//计算出所有情况 {t.reset();for(int i=1;i<=m;i++){cin>>x;t|=b<<x;}b=t;}int tg,ans=INT_MAX;cin>>tg;for(int i=0;i<5050;i++)//一共就5000中可能的分数,所以直接遍历即可不会超 {if(b[i])ans=min(ans,abs(i-tg));}cout<<ans;
}
signed main()
{IOS;int _=1;
//	cin>>_;while(_--)solve();return 0;
}

关于bitset优化dp,求总共多少情况类型的题,可以参考这个模板:

bitset的核心步骤bitset<N> b, t;b.set(0);for(int i = 1; i <= n; i ++ ) {t.reset();for(int j = 1; j <= m; j ++ ) {int x; cin >> x;t |= (b << x);}b = t;}

文章转载自:
http://catalpa.alwpc.cn
http://bullhorn.alwpc.cn
http://atmometry.alwpc.cn
http://buccinator.alwpc.cn
http://armoured.alwpc.cn
http://armourbearer.alwpc.cn
http://ablepsia.alwpc.cn
http://bailor.alwpc.cn
http://autotelegraph.alwpc.cn
http://bioplasm.alwpc.cn
http://arts.alwpc.cn
http://adolescency.alwpc.cn
http://afferently.alwpc.cn
http://astrocyte.alwpc.cn
http://airborne.alwpc.cn
http://acataleptic.alwpc.cn
http://anisotropic.alwpc.cn
http://argumentation.alwpc.cn
http://anaclitic.alwpc.cn
http://blusher.alwpc.cn
http://calisthenics.alwpc.cn
http://bilbo.alwpc.cn
http://annunciator.alwpc.cn
http://alamine.alwpc.cn
http://aftersensation.alwpc.cn
http://chlordane.alwpc.cn
http://antiatom.alwpc.cn
http://accidentalist.alwpc.cn
http://agonist.alwpc.cn
http://cakewalk.alwpc.cn
http://www.dtcms.com/a/281510.html

相关文章:

  • “C21988-谷物烘干机(2D+3D+说明书+运动仿真)8张cad+设计说明书
  • Eplan API SQL
  • 从灾前感知到灾后恢复:人工智能在城市气候风险管理中的全链路赋能
  • ESLint 除了在packages.json还能在哪里配置?
  • 【插件】vue-i18n的安装和使用全解
  • Nvidia服务器备份指南 (数据+环境)
  • 高防CDN与普通CDN的核心区别
  • DevOps落地的终极实践:8大关键路径揭秘!
  • Python 字典 (Dictionary) 详解
  • AI产品经理面试宝典第18天:AI思维矩阵构建与实战应用面试题与答法
  • 2HDMI/1DP转EDP/LVDS,支持4K,144HZ和240HZ.
  • zynq分频的例子
  • python学智能算法(十九)|SVM基础概念-超平面
  • Python语法入门之装饰器的基本用法
  • 硬件设计学习DAY1——电源的分类
  • js运算符
  • Java线程池深度解析与Spring Boot实战指南
  • 文献阅读 250715-Atmospheric rivers cause warm winters and extreme heat events
  • 服务器数据恢复—光纤存储硬盘扇区不稳定导致业务中断如何恢复数据?
  • 【物联网】基于树莓派的物联网开发【9】——树莓派修正系统时间五种解决方案
  • next.js 登录认证:使用 github 账号授权登录。
  • Postgresql源码(147)Nestloop流程与Instrumentation简单分析
  • 24.找到列表中最大或最小值的索引
  • 什么是私有化部署企业即时通讯?吱吱企业即时通讯安全吗?
  • frp内网穿透(二)
  • 【实时Linux实战系列】实时嵌入式系统开发基础
  • Java的stream流其一
  • 美图RoboNeo发布:一句话修图建站 | AI早报
  • Rembg开源项目全面解析:从原理到实践应用
  • 银河麒麟(Kylin) - V10 GFB高级服务器操作系统ARM64部署昇腾910b训练机以及Docker安装