1.基础-01/完全/多重/混合
1282. 简单背包问题
从0开始
#include <iostream>
#include <vector>
using namespace std;
int knapsack(int maxw, int n, vector<int>& w, vector<int>& v) {
vector<vector<int>> dp(n + 1, vector<int>(maxw + 1, 0));
// 构建动态规划表
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= maxw; j++) {
if (j < w[i - 1]) {
dp[i][j] = dp[i - 1][j];
} else {
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i - 1]] + v[i - 1]);
}
}
}
return dp[n][maxw];
}
int main() {
int maxw, n;
cin >> maxw >> n;
vector<int> w(n), v(n);
for (int i = 0; i < n; i++) {
cin >> w[i] >> v[i];
}
cout << knapsack(maxw, n, w, v) << endl;
return 0;
}
从1开始
#include <iostream>
#include <vector>
using namespace std;
int knapsack(int maxw, int n, vector<int>& w, vector<int>& v) {
vector<vector<int>> dp(n + 1, vector<int>(maxw + 1, 0));
// 构建动态规划表,从1开始
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= maxw; 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]);
}
}
}
return dp[n][maxw];
}
int main() {
int maxw, n;
cin >> maxw >> n;
vector<int> w(n + 1), v(n + 1);
// 从索引1开始读取数据
for (int i = 1; i <= n; i++) {
cin >> w[i] >> v[i];
}
cout << knapsack(maxw, n, w, v) << endl;
return 0;
}
一维优化
- 背包最大载重
maxw = 10
- 物品总数
n = 3
- 物品及其价值如下:
- 物品 1:重量 4,价值 5
- 物品 2:重量 3,价值 4
- 物品 3:重量 6,价值 9
初始化 dp
数组为 0,dp
的大小为 maxw + 1 = 11
,代表可能的载重从 0 到 10。
dp[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] // 初始状态,最大载重从 0 到 10
- 添加物品 1:重量 4,价值 5
- 我们从后向前考虑,只有当
j >= 4
时,我们才能选择是否加入物品 1。
- 我们从后向前考虑,只有当
遍历开始,添加物品 1:
dp[0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5] // 重量4及以上的位置更新为5,代表加入物品1的价值
- 添加物品 2:重量 3,价值 4
- 同样,从后向前考虑,当
j >= 3
时,可以选择是否加入物品 2。我们需要比较不加入物品 2 和加入物品 2 的价值。
- 同样,从后向前考虑,当
遍历开始,添加物品 2:
dp[0, 0, 0, 4, 5, 5, 5, 9, 9, 9, 9] // 重量3的位置更新为4,重量6及以上的位置考虑加入物品2的价值
- 添加物品 3:重量 6,价值 9
- 当
j >= 6
时,考虑加入物品 3。比较不加入和加入物品 3 后的价值。
- 当
遍历开始,添加物品 3:
dp[0, 0, 0, 4, 5, 5, 9, 9, 9, 9, 14] // 重量6的位置更新为9,重量10的位置加入物品3,总价值更新为14
在最后,dp[10] = 14
,这是背包最大载重为 10 时能装入物品的最大价值。
这个过程展示了如何通过一维 dp
数组迭代更新每种载重下的最大价值,最终找到在给定最大载重限制下,能够获得的最大价值。## 1780. 采灵芝
这个问题是一个典型的完全背包问题。在完全背包问题中,每种物品可以选择无限次,直到背包容量(在这个问题中是时间限制)达到上限。我们需要找到一个方法来最大化在给定时间限制内采摘的灵芝总价值。
我们可以使用动态规划来解决这个问题。下面是一个使用一维数组进行动态规划的 C++ 解决方案:
#include <iostream>
#include <vector>
using namespace std;
int main() {
int T, M;
cin >> T >> M;
vector<int> dp(T + 1, 0);
for (int i = 0; i < M; i++) {
int time, value;
cin >> time >> value;
for (int j = time; j <= T; j++) {
dp[j] = max(dp[j], dp[j - time] + value);
}
}
cout << dp[T] << endl;
return 0;
}
在这个程序中,我们首先读取总时间 T
和灵芝种类数 M
。然后,我们为每种灵芝读取它的采摘时间和价值,并更新动态规划数组 dp
。数组 dp[j]
表示在时间 j
内可以采摘的灵芝的最大总价值。对于每种灵芝,我们更新从它的采摘时间开始到总时间 T
的每一个时间点的最大价值。最后,dp[T]
就是我们要找的在规定时间内可以采到的灵芝的最大总价值。
在动态规划中,将二维数组优化为一维数组的关键在于理解如何使用一维数组来替代二维数组的角色,同时避免在迭代过程中对同一轮次的状态进行覆盖。
在背包问题中,如果使用二维数组 dp[i][j]
表示考虑到第 i
个物品时,容量为 j
的背包所能装入的最大价值,我们可以转化为一维数组 dp[j]
,其中 dp[j]
仅表示容量为 j
的背包所能装入的最大价值。
完全背包问题的一维数组优化需要特别注意循环的顺序。与 0-1 背包问题不同,完全背包问题中,每种物品可以被多次选择。因此,在更新 dp[j]
时,我们应该正向遍历,从而确保每种物品可以被重复选取。
以下是完全背包问题的一维数组优化方法:
-
初始化一个一维数组
dp
,其长度为背包的容量加一,所有元素初始化为 0。 -
对于每种物品,从该物品的重量开始,正向遍历到背包的最大容量。在每个容量点更新
dp[j]
为max(dp[j], dp[j - weight] + value)
,其中weight
和value
分别是当前物品的重量和价值。这样确保了每种物品可以被多次选择。
通过以上步骤,我们能够使用一维数组来完成原本需要二维数组来完成的任务,从而优化空间复杂度。
二维代码
#include <iostream>
#include <vector>
using namespace std;
int main() {
int T, M;
cin >> T >> M;
vector<vector<int>> dp(M + 1, vector<int>(T + 1, 0));
for (int i = 1; i <= M; i++) {
int time, value;
cin >> time >> value;
for (int j = 0; j <= T; j++) {
if (j < time) {
dp[i][j] = dp[i - 1][j];
} else {
dp[i][j] = max(dp[i - 1][j], dp[i][j - time] + value);
}
}
}
cout << dp[M][T] << endl;
return 0;
}