# 多重背包
今天蛋糕店的活动已经结束了,开始了正常的营业。于是我们又遇到了新问题。
蛋糕店现出售`n`种蛋糕🎂,每种蛋糕都有三种属性`cost[i]`,`val[i]`,`num[i]`,分别表示第`i`种蛋糕的`单价`,`喜爱值`,`数量`。我现在只有`v`元钱,我希望能够买到的蛋糕的`总喜爱值`尽可能的大,那么我应该怎么样购买呢?
## 分析
### 解法1
很容易想到一种非常`暴力`的解法,将每种蛋糕都拆分成`num[i]`个,然后当成01背包来做就好了,不过当`num[i]`过大时,拆分物品的数量太多,会花费太长的时间。
### 解法2
可以使用如下方法来减少拆分的数量:
>考虑十进制下(15)可以表示成二进制下的(1111),那么如果把15拆成(1000),(0100),(0010),(0001),也就是`1,2,4,8`这样就可以表示15一下的所有整数了。
对于某些二进制形式不全为1的数字,比如14(1110),应该从`1,2,4,8,...`的顺序来拆分,最后剩余的部分直接保留,也就是拆分成`1,2,4,7`,而不是拆成`1,4,8`。
用这种二进制拆分法来拆分每种物品,每种物品最后会被拆分成`log2(num[i])`个左右,一般来说如果`num[i]<1e9`,那么就是`20~30`个,这是可以接受的。拆分后按照01背包的解法就可以解决这个问题了。
### 二进制拆分复杂度及代码
时间复杂度:O(log2(n))
空间复杂度:O(log2(n))
代码:
~~~py
n = int(input())
res = [] # 拆分结果
m = 100 # log2(MAX_N)
for i in range(m):
if n>(1<<i):
res.append(1<<i)
n -= 1<<i
if n: # 可能会有剩余
res.append(n)
print(res)
~~~
- 简介
- 零基础快速入门ACM
- C语言快速入门
- C语言快速入门
- C/C++基础
- 输入输出
- 数组和字符串
- 数组
- 字符数组
- 函数和递归
- C++标准容器库(STL)
- Vector
- Map
- Set
- String
- Stack
- Queue
- Priority_queue
- 贪心
- 硬币问题
- 区间调度问题
- 字典序最小问题
- 独木舟问题
- 搜索
- DFS
- BFS
- 剪枝
- 记忆化搜索
- 常用技巧
- 倍增
- 高精度
- 大数加法
- 大数减法
- 大数乘法
- 大数除法
- 大数阶乘
- 输入挂
- 前缀和
- 二分
- 本地预处理
- 尺取
- 枚举
- 坐标离散化
- 分治
- 数列分治
- 树上分治
- 平面分治
- 计算几何
- 凸包
- 点积
- 叉积
- 线段关系
- 皮克定理
- 最近点对
- 数据结构
- 线段树
- 树状数组
- 并查集
- 动态规划
- 基础知识
- 信息学竞赛中的动态规划
- 动态规划初步
- 最长上升子序列(LIS)
- 最长公共子序列(LCS)
- 最大子段和
- 背包问题
- 部分背包
- 0 1 背包
- 完全背包
- 多重背包
- 背包的可行性问题
- 线性DP
- 树形DP
- 区间DP
- 数位DP
- 动态规划优化
- 字符串
- KMP算法
- 拓展KMP
- 字符串Hash
- Manacher算法
- 后缀数组
- Trie树
- 博弈论
- Nim博弈
- Bash博弈
- 斐波那契博弈
- 威佐夫博弈
- SG函数
- 数论
- 整数研究
- 素数判断
- 素数筛选
- 快速幂
- 算数基本定理
- 最大公约数(Gcd)
- 费马小定理
- 扩展欧几里得
- 逆元
- 同余定理
- 组合数学
- 容斥原理
- 抽屉原理
- 卢卡斯(Lucas)
- 卡特兰(Catalan)
- 著名函数
- 欧拉函数
- 莫比乌斯函数
- 线性代数
- 矩阵运算
- 矩阵快速幂
- 图论
- 存图方法
- 邻接矩阵
- 邻接表
- Vector存图
- 链式前向星
- 图的遍历
- 拓扑排序
- 最短路
- Dijkstra算法
- SPFA算法
- Floyed 算法
- 最小生成树
- Prim算法
- Kruskal算法
- 最近公共祖先 (LCA)
- 二分图匹配
- 网络流
- 其他
- 莫队