使用bitset优化0/1背包的时空复杂度。(100320. 执行操作可获得的最大总奖励 II)

被leetcode401单周赛狠狠打败了。就此记录下bitset优化0-1背包。利用数字的二进制操作对整体进行转移

问题描述

点击链接直接获取问题描述

求解思路

状态定义

定义dp[i][j]表示能否从前i个数中得到总奖励j。每次转移对前一个状态的j有特殊要求。

状态转移方程

考虑第i个物品选或不选:

  • 不选第i个物品:dp[i][j]=dp[i1][j]
  • 选择第i个物品:dp[i][j]=dp[i1][jw[i]] ,需要满足jw[i]jw[i]<w[i]
    状态为能否从前i个数中得到奖励,即dp[i][j]=dp[i1][j]  dp[i1][jw[i]]
初始化方式

初始值dp[0][0]=true
所求答案为dp[n][j]=true中最大的j。

优化方式

对应的状态个数为n2,当然是过不了当前n=2×105 的数据。需要优化掉其中一个维度。

  1. 由于状态转移中第i层一定从第i1层转移过来,可以两个数组或者原地滚动进行优化。但只省下空间,总状态数还是那么多,没啥用

  2. 注意到dp[i][j]一定是从dp[i1][j]dp[i1][jw[i]]转移过来,对应的0jw[i]<w[i]。也就是存在一一对应的关系,并且转移方式为两者取或。使用bitset对优化当前0/1背包的状态转移。

    二进制数表示每个状态,二进制数位j上的数字表示当前状态下的j能否被取到。 对应的状态转移为了满足限制条件,需要先将x中数位大于j的位置设置为0,再将当前的二进制数x与左移w[i]位的x取或。初始状态设置为1,代表开始只有0满足要求。

bitset介绍

该部分内容搬运自 -Wallace-的文章

它类似于 bool 数组,每个位置只有两种值:0 或 1。
bitset 的实现方式是压位,那么一个大小为 n𝑛 的 bitset 的空间复杂度为 O(1ωn)。其中 ω=32 或 64(系统位数)。

基本操作
bitset<N> f;//定义一个大小为N的bitset,下标范围[0, N)
f.set(i);//在下标i设为1
f.reset(i);//将下标i设置为0
f.test(i);//判断下标i处是否为1
f[i];//取值

除了构造函数,其余操作均为O(1)

进阶操作
f.set(); // 全部置为 1 
f.reset(); // 全部置为 0 
f = g; // bitset 赋值 
f &= g; // 将 f 对 g 做按位与操作 
f |= g; // 将 f 对 g 做按位或操作 
f ^= g; // 将 f 对 g 做按位异或操作 
// 以及各种位运算操作 
f.count(); // 计算 bitset 中 1 的个数

这些范围操作都是O(1ωn)的时间复杂度。bitset时空复杂度中的常数1w 是bitset优化的关键。

代码案例

源自灵茶山艾府

bitset<100000>f{1};//初始化下标0的位置为1
for (int v : rewardValues) {
    int shift = f.size() - v;
    //bitset的区间操作带有常数1/w
	f |= f << shift >> shift << v;
}
for (int i = rewardValues.back() * 2 - 1; ; i--) {
		//获取最大的下标为1的位置
        if (f.test(i)) {
            return i;
        }
    }

使用bitset优化后,对应的时间复杂度为(1wn2) ,w对应为32或64,满足数据范围n=2×105的要求。

posted @   tanch25  阅读(148)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示