一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第27天,点击查看活动详情。
特点
- 有N个物品,容量是V的背包,每个物品有两个属性,体积Vi,价值Wi
- 每个物品个数限制不一样,每个物品最多有Si个
- 求:在背包能装的下的情况下的最大价值是多少
会讲一个朴素版跟优化版
题目
分析
状态计算:
- 第i个物品的选法,0个,1个,2个...但是个数不能超过s[i](有个数限制)
代码
朴素版
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 110;
int n, m;
int v[N], w[N], s[N];
int f[N][N];
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> v[i] >> w[i] >> s[i];
for (int i = 1; i <= n; i++)
for (int j = 0; j <= m; j++)
for (int k = 0; k <= s[i] && k * v[i] <= j; k++)
f[i][j] = max(f[i][j], f[i - 1][j - v[i] * k] + w[i] * k);
cout << f[n][m] << endl;
return 0;
}
题目
优化做法:二进制优化方式
- 每种物品最多有Si个
- 将他们进行分组,拆分成logSi个组
- 分组的原理:
- 分组,每组分别只有这么多个物品:1, 2, 4, 8,...,2^k,C(常数),每组里面只能选一个物品
- C < 2^(k + 1)
- 1 + 2 + ……+2^k <= S
- 如果
1 + 2 + …… + 2^k之和比S小的话,要补一个C,使得1 + 2 + …… + 2^k + C= S
- 范围的扩展:0~1, 1
- 分组,每组分别只有这么多个物品:1, 2, 4, 8,...,2^k,C(常数),每组里面只能选一个物品
- 分组的原理:
- 然后将拆分出来的组转化成01背包问题
- 时间复杂度:
- 没优化之前:
N*V*S - 优化之后:
N*V*logS
- 没优化之前:
代码
#include <iostream>
#include <algorithm>
using namespace std;
// N:物品个数1000 * log2000
const int N = 25000, M = 2010;
int n, m;
int v[N], w[N];
int f[N];
int main()
{
cin >> n >> m;
// 进行分组
int cnt = 0; // 用一个变量来存储我们所有新的物品(分组后组内的物品)
for (int i = 1; i <= n; i++)
{
int a, b, s; // 读入当前物品的体积,价值和个数
cin >> a >> b >> s;
int k = 1; // 从1开始分
while (k <= s)
{
cnt ++; // 先将当前的物品编号++
v[cnt] = a * k; // 分组后的物品的体积
w[cnt] = b * k; // 分组后物品的价值
s -= k;
k *= 2;
}
if (s > 0) // 说明我们还要补上C
{
cnt ++;
v[cnt] = a * s;
w[cnt] = b * s;
}
}
n = cnt;
// 做一次01背包问题就行了
for (int i = 1; i <= n; i++)
for (int j = m; j >= v[i]; j--)
f[j] = max(f[j], f[j - v[i]] + w[i]);
cout << f[m] << endl;
}