AcWing 5. 多重背包问题 II
题目
有 \(N\) 种物品和一个容量是 \(V\) 的背包。
第 \(i\) 种物品最多有 \(s_i\) 件,每件体积是 \(v_i\),价值是 \(w_i\)。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,\(N,V\),用空格隔开,分别表示物品种数和背包容积。
接下来有 \(N\) 行,每行三个整数 \(v_i, w_i, s_i\),用空格隔开,分别表示第 \(i\) 种物品的体积、价值和数量。
输出格式
输出一个整数,表示最大价值。
数据范围
\(0 \lt N \le 1000\)
\(0 \lt V \le 2000\)
\(0 \lt v_i, w_i, s_i \le 2000\)
提示:
本题考查多重背包的二进制优化方法。
输入样例
4 5
1 2 3
2 4 1
3 4 3
4 5 2
输出样例:
10
题解
C++ 代码
/* 将多重背包问题进行二进制优化 至于为什么不能像完全背包一样利用f[i,j]和f[i,j-v]状态方程优化,
因为f[i,j]比f[i,j-v]多了一项,优化不掉,不懂的话看底部提供的题解 ,所以本题我们使用二进制优化 */
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 11010, M = 2010; //这里N开的数据范围的依据:多重背包问题中的一个「箱子」相当于01背包问题中的一件「物品」,实际上我们是要将s[i]用几个箱子装进去,因此我们需要估计出多重背包问题中到底有多少个箱子。
//这里的箱子也就是二进制优化下的s[i] 由题 s[i]<=2000 则log2000 < 11(log以2为底) 一共最多1000件物品,则我们需要开的范围就是 11*1000+10=11010
int v[N], w[N];
int f[M]; //f[]是装不同背包容积下的物品最大价值,所以用背包容积来开f[]数组
int main()
{
int n,m;
scanf("%d%d", &n, &m);
int cnt = 0; //重新定义存储物品的编号
for (int i = 1; i <= n; i ++ )
{
int a, b, s;
scanf("%d%d%d", &a, &b, &s); //依次录入i号物品的体积 价值 数量
int k = 1; //定义箱子可以放物品的初始值 第一个箱子可以放一个i号物品
while (k <= s)
{
cnt ++ ; //编号增加 可以看成是每一个箱子的编号
v[cnt] = a * k; //这个箱子可以存的i号物品总体积 箱子可以放的物品总数 * 物品体积 = 物品总体积
w[cnt] = b * k; //这个箱子可以存的i号物品总价值
s -= k; //从i号物品总个数中减去这个箱子装的k个 方便while循环条件判断
k *= 2; //二进制优化 下一个箱子可以放的物品数量是上一个的两倍
}
if (s > 0) //如果物品i还没有被二进制箱子存完 再开一个可以存还剩下的所有物品i的箱子
{
cnt ++ ; //当前箱子的编号
v[cnt] = a * s;
w[cnt] = b * s;
}
}
n = cnt; //关键一步!将物品编号更改为所有物品在箱子里装完后箱子的编号
//上面结束后 我们将物品全部装进了箱子 我们不管想在背包里装多少个物品i,都可以装箱子的方式实现
//例如要装7个物品i 我们就用物品i的1号2号3号箱子(1号能装1个i 2号能装2个i 3号能装4个i)
//这就是我们使用二进制优化的意义,能将所有需要的物品数量表示出来
//由于所有需要的物品数量都能用不同箱子的组合表达 所以现在每个箱子的状态只能是 用或者不用 从而转化为01背包问题
//对照01背包代码
for (int i = 1; i <= n; i ++ ) //注意n已经被cnt重新赋值了
for (int j = m; j >= v[i]; j -- ) //01背包逆序存储
f[j] = max(f[j], f[j - v[i]] + w[i]);
printf("%d\n", f[m]);
return 0;
}
https://www.acwing.com/solution/content/20115/
https://blog.csdn.net/raelum/article/details/128996521

浙公网安备 33010602011771号