01背包问题,你该了解这些!
题目链接:46. 携带研究材料(第六期模拟笔试)
文档讲解:代码随想录
视频讲解:带你学透0-1背包问题!| 关于背包问题,你不清楚的地方,这里都讲了!| 动态规划经典问题 | 数据结构与算法
思路
- 确定dp数组以及下标的含义:dp[i][j]表示从下标为[0 - i]的物品里任意取,放进容量为j的背包,价值总和最大值
- 确定递推公式:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]),不取第i个物品,则背包容量j中取[0 ~ i - 1]的价值最大值,取第i个物品,则背包容量j - weight[i]中取[0 ~ i - 1]的价值最大值+第i个物品的价值,两者的最大值
- dp数组如何初始化:第一列初始化为0,第一行背包容量大于第0个物品重量的位置取第一个物品的价值
- 遍历顺序:先遍历背包或者先遍历物品皆可
代码
左闭右开区间代码
int main() {
// M表示物品个数,N表示背包容量
int M, N;
cin >> M >> N;
vector<int> weights(M, 0);
vector<int> values(M, 0);
for (int i = 0; i < M; i++)
cin >> weights[i];
for (int i = 0; i < M; i++)
cin >> values[i];
vector<vector<int>> dp(M, vector<int>(N + 1, 0));
// 初始化,第一列背包容量为0,价值为0
// 初始化,第一行放物品1,如果能放下,则价值为物品1的价值
for (int i = weights[0]; i <= N; i++)
dp[0][i] = values[0];
for (int i = 1; i < M; i++) { // 遍历物品
for (int j = 0; j <= N; j++) { // 遍历背包
if (j < weights[i])
dp[i][j] = dp[i - 1][j];
else
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weights[i]] + values[i]);
}
}
cout << dp[M - 1][N] << endl;
return 0;
}
01背包问题,你该了解这些! 滚动数组
题目链接:46. 携带研究材料(第六期模拟笔试)
文档讲解:代码随想录
思路
由于二维数组dp[i][j]中,第i行只用到了第i - 1行的数据,因此可以把dp[i - 1]行的数据拷贝到dp[i]行上。
- 确定dp数组以及下标的含义:dp[j]表示背包容量为j的背包,物品价值的最大值为dp[j]
- 确定递推公式:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]),不取第i个物品,则背包容量j中取[0 ~ i - 1]的价值最大值,取第i个物品,则背包容量j - weight[i]中取[0 ~ i - 1]的价值最大值+第i个物品的价值,两者的最大值
- dp数组如何初始化:全部初始化为0
- 遍历顺序:先遍历物品,再从后向前遍历背包
代码
int main() {
// M表示物品个数,N表示背包容量
int M, N;
cin >> M >> N;
vector<int> weights(M, 0);
vector<int> values(M, 0);
for (int i = 0; i < M; i++)
cin >> weights[i];
for (int i = 0; i < M; i++)
cin >> values[i];
vector<int> dp(N + 1, 0);
for (int i = 0; i < M; i++) { // 遍历物品
for (int j = N; j > 0; j--) { // 遍历背包
if (j >= weights[i])
dp[j] = max(dp[j], dp[j - weights[i]] + values[i]);
}
}
cout << dp[N] << endl;
return 0;
}
416. 分割等和子集
题目链接:416. 分割等和子集
文档讲解:代码随想录
思路
将问题转化为01背包问题,背包的体积为sum / 2;背包放入物品的重量为元素的数值,价值也为元素的数值;背包正好装满,说明找到了总和为sum / 2的子集;背包中每一个元素不可重复放入
- 确定dp数组以及下标的含义:dp[j]表示背包总容量是j,放进物品后,背的最大重量是dp[j]
- 确定递推公式:dp[j] = max(dp[j], dp[j - nums[i] + nums[i)
- dp数组如何初始化:全部初始化为0
- 遍历顺序:先遍历物品,再从后向前遍历背包
代码
class Solution {
public:
bool canPartition(vector<int> &nums) {
int sum = 0;
for (auto i : nums)
sum += i;
if (sum % 2 == 1)
return false;
vector<int> dp(sum / 2 + 1, 0);
for (int i = 0; i < nums.size(); i++) {
for (int j = sum / 2; j >= nums[i]; j--) {
dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);
}
}
if (dp[sum / 2] == sum / 2)
return true;
return false;
}
};