🧑💻 文章作者:Iareges
🔗 博客主页:https://blog.csdn.net/raelum
⚠️ 转载请注明出处
目录
- 前言
- 一、01背包
- 1.1 使用滚动数组优化
- 二、完全背包
- 2.1 使用滚动数组优化
- 三、多重背包
- 3.1 使用二进制优化
- 四、分组背包
- 总结
前言
本文主要介绍常见的四种背包问题,思维导图如下:
一、01背包
💡 现有 件物品和一个最多能承重 的背包,第 件物品的重量是 ,价值是 。在背包能承受的范围内,试问将哪些物品装入背包后可使总价值最大,求这个最大价值。
因为每件物品只有选与不选两种状态,所以该问题又称01背包问题。
设 的含义是:在背包承重为 的前提下,从前 个物品中选能够得到的最大价值。不难发现 就是本题的答案。
如何计算 呢?我们可以将它划分为以下两部分:
- 选第 个物品:由于第 个物品一定会被选择,那么相当于从前 个物品中选且总重量不超过 ,对应 。
- 不选第 个物品:意味着从前 个物品中选且总重量不超过 ,对应 。
结合以上两点可得递推公式:
由于下标不能是负数,所以上述递推公式要求 。当 时,意味着第 个物品无法装进背包,此时 。综合以上可得出:
数组应当如何初始化呢?当背包承重为 时,显然装不下任何物品,所以 。若一个物品也不选(即从前 个物品中选),此时最大价值也是 ,所以 。由此可知, 数组应当全0初始化,即声明为全局变量。
题目链接:AcWing 2. 01背包问题
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int w[N], v[N];
int dp[N][N];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> w[i] >> v[i];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (j < w[i]) dp[i][j] = dp[i - 1][j];
else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);
}
}
cout << dp[n][m] << "\n";
return 0;
}
时间复杂度为 。
1.1 使用滚动数组优化
之前我们用到的 数组是二维数组,它可以进一步优化成一维数组。
观察递推公式不难发现, 数组中第 行的元素仅由第 行的元素得来,即第 行元素的更新值放到第 行,第 行元素的更新值放到第 行,以此类推。与其把一行的更新值放到新的一行,不如直接就地更新,因此我们的 数组只需要一行来存储,即一维数组。
去掉 数组的第一维后,递推公式变成:
即
原先 是从 遍历至 的,现在只需从 遍历至 。但,这个遍历顺序真的对吗?
请看下图:
文章出处登录后可见!
已经登录?立即刷新