1. 01背包
现有 \(n\) 个物品, 已知第 \(i\) 个物品的价值为 \(w_i\), 体积为 \(v_i\), 你有一个体积为 \(m\) 的背包, 问: 使用该背包能带走的物品的价值最多是多少
1.1 考虑状态表示
设状态 \(f_{i, j}\) 表示对于前 \(i\) 个物品, 容量为 \(j\) 的背包所能装下的最大价值
1.2 考虑状态转移方程
我们可以发现, 对于第 \(i\) 个物品, 我们有两种选择:
- 装入背包, 此时背包中所有物品的价值为 \(f_{i - 1, j - v_i} + w_i\)
- 不装入背包, 此时背包中所有物品的价值为 \(f_{i - 1, j}\)
对于第 \(i\) 个物品, 我们选择价值最大的那个选择: \(f_{i, j} = max(f_{i - 1, j}, f_{i - 1, j - v_i} + w_i)\)
1.3 代码实现
1.3.1 没有使用滚动数组, 空间复杂度为 \(O(nm)\)
for (int i = 1; i <= n; ++ i)
for (int j = 0; j <= m; ++ j)
if (j < v[i]) f[i][j] = f[i - 1][j];
else f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i]);
cout << f[n][m] << endl; // f[n][m] 表示体积为 m 的背包对于前 n 个物品, 所能装走的最大的价值
1.3.2 使用滚动数组, 空间复杂度为 \(O(m)\)
我们可以发现, 对于上一段代码
- 在 \(j < v_i\) 时, \(f_{i, j}\) 直接继承 \(f_{i - 1, j}\) 的值
- 在 \(j \geq v_i\) 时, \(f_{i, j}\) 取的是 \(f_{i - 1, j}\) 和 \(f_{i - 1, j - v_i} + w_i\) 之间的最大值(都是第 \(i - 1\) 层)
故尝试去掉 \(f\) 的第一维
for (int i = 1; i <= n; ++ i)
for (int j = v[i]; j <= m; ++ j)
f[j] = max(f[j], f[j - v[i]] + w[i]);
然而此时, 我们发现: 每当使用 \(f_{j - v_i} + w_i\) 更新 \(f_j\) 时, 这个 \(f_{j - v_i} + w_i\) 已经不是 \(f_{i - 1, j - v_i} + w_i\), 而是 \(f_{i, j - v_i} + w_i\) 了, 故第二层循环需要逆序
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; // f[m] 表示体积为 m 的背包对于前 n 个物品, 所能装走的最大的价值
标签:01,1.3,int,复杂度,背包,物品,价值 来源: https://www.cnblogs.com/suzukaze/p/01KP.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。