背包问题的重点突破,01背包问题,从二维数值转变为一维数组。 开始五步法: 1.设计dp数组的含义,区分下标和dp[i]内部数值各自的含义。在背包问题中,下标i指背包中物品的容量,dp[i]指该容量条件下所能承担的最大价值 2.确定递推关系,二维数组和一维数组之间的本质关系相同,都是采用动态规划,获取上一步的结果之后再对下一步进行递推。在二维数组中,dp[i][j]=max(dp[i-1][j],dp[i][j-weight[i]]+value[i]);这里dp[i][j]有两个选择,选择dp[i][j]是不装入物品i,选择dp[i][j-weight[i]]+value[i]是装入物品i,也应当前置判断是否可以装入物品i。 在一维数组中,可以注意到可以将i简化dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);注意,这里始终都是双层循环,从最轻物品i开始外层循环,相当于进行了第一行的横轴初始化。 3.根据需求进行初始化。01背包问题的初始化首先将dp数组初始化为0,根据是二维数组还是一维数组开始进行初始化。这里先讲简单的二维数组初始化: 3.1 二维数组初始化重点初始化横轴和纵轴,横轴仅仅装入最轻物品1,全部初始化为物品1的价值,再初始化纵轴,在背包容量为0的条件下,dp[i]纵向应当均为0; 3.2 一维滚动数组的初始化,可以非常简单的初始化为0即可,后续数值在递归关系中可以获取 4.确定遍历顺序。 4.1二维数组无论是先物品再背包容量还是先背包容量再物品都是可以的。但是常规思想是先物品然后考察背包容量方便理解。 4.2一维数组存在重大差异。当物品不可重复时,内层循环必须自背包容量大到小遍历,否则会出现同一物品二次装入的情况!!! 5.举例子画表格进行推导,看看是否符合要求
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, bagweight;// bagweight代表行李箱空间
cin >> n >> bagweight;
vector<int> weight(n, 0); // 存储每件物品所占空间
vector<int> value(n, 0); // 存储每件物品价值
for(int i = 0; i < n; ++i) {
cin >> weight[i];
}
for(int j = 0; j < n; ++j) {
cin >> value[j];
}
// dp数组, dp[i][j]代表行李箱空间为j的情况下,从下标为[0, i]的物品里面任意取,能达到的最大价值
vector<vector<int>> dp(weight.size(), vector<int>(bagweight + 1, 0));
// 初始化, 因为需要用到dp[i - 1]的值
// j < weight[0]已在上方被初始化为0
// j >= weight[0]的值就初始化为value[0]
for (int j = weight[0]; j <= bagweight; j++) {
dp[0][j] = value[0];
}
for(int i = 1; i < weight.size(); i++) { // 遍历科研物品
for(int j = 0; j <= bagweight; j++) { // 遍历行李箱容量
if (j < weight[i]) dp[i][j] = dp[i - 1][j]; // 如果装不下这个物品,那么就继承dp[i - 1][j]的值
else {
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
}
}
}
cout << dp[n - 1][bagweight] << endl;
return 0;
}
416.分割等和子集问题 参考01背包问题即可解决
class Solution {
public:
bool canPartition(vector<int>& nums) {
//剪支处理
if(nums.size()<=1) return false;
//排除奇数情况
int sum=0;
for(int i=0;i<nums.size();i++){
sum+=nums[i];
}
if(sum%2==1) return false;
//拆分为01背包问题
//目标期望
int target=sum/2;
//设计dp数组,考虑到nums.length<=200;nums[i]<=100,这里将数组下标设计为物品重量
//当dp[target]==target时,即满足题设条件
vector<int> dp(10001,0);//最大值和为20000的一半,初始化为0;
//执行递推关系
for(int i=0;i<nums.size();i++){//从物品开始遍历
for(int j=target;j>=nums[i];j--){//注意这里的截至条件时nums[i],不仅是提前剪支,也是在防止物品二次装入
dp[j]=max(dp[j],dp[j-nums[i]]+nums[i]);
}
}
//结果判断
if(dp[target]==target) return true;
return false;
}
};