01背包问题之一维dp数组:
01背包问题二维dp数组:dp[i][j]表示任选物品0 ~ i放进容量为j的背包里所能获得的最大价值,其中dp递推公式为:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]),其中如果不放物品i,那么最大价值为任选物品0 ~ i - 1放进容量为j的背包里所能获得的最大价值;如果放物品i,那么最大价值为任选物品0 ~ i - 1放进容量为j - weight[i]的背包里加上物品i的价值所能获得的最大价值。遍历顺序:先遍历物品,再遍历容量,正序遍历。
将二维dp数组压缩成一维dp数组:dp[j]表示容量为j的背包,所背的物品价值可以最大为dp[j]。dp递推公式:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]),此时dp[j]有两个选择,一个是取自己dp[j] 相当于 二维dp数组中的dp[i-1][j],即不放物品i,一个是取dp[j - weight[i]] + value[i],即放物品i,指定是取最大的,毕竟是求最大价值。遍历顺序:先遍历背包容量,再遍历物品,其中遍历背包容量是倒序遍历。
至于为什么背包容量是倒序遍历:for(int j = bagweight; j >= weight[i]; j--)?
首先要明白二维数组的递推过程,然后才能看懂二维变一维的过程。
假设目前有背包容量为10,可以装的最大价值, 记为g(10)。
即将进来的物品重量为6。价值为9。
那么此时可以选择装该物品或者不装该物品。
如果不装该物品,显然背包容量无变化,这里对应二维数组,其实就是取该格子上方的格子复制下来,就是所说的滚动下来,直接g【10】 = g【10】,这两个g【10】要搞清楚,右边的g【10】是上一轮记录的,也就是对应二维数组里上一层的值,而左边是新的g【10】,也就是对应二维数组里下一层的值。
如果装该物品,则背包容量= g(10-6) = g(4) + 9 ,也就是 g(10) = g(4) + 6 ,这里的6显然就是新进来的物品的价值,g(10)就是新记录的,对应二维数组里下一层的值,而这里的g(4)是对应二维数组里上一层的值,通俗的来讲:你要找到上一层也就是上一状态下 背包容量为4时的能装的最大价值,用它来更新下一层的这一状态,也就是加入了价值为9的物品的新状态。
这时候如果是正序遍历会怎么样? g(10) = g(4) + 6 ,这个式子里的g(4)就不再是上一层的了,因为你是正序啊,g(4) 比g(10)提前更新,那么此时程序已经没法读取到上一层的g(4)了,新更新的下一层的g(4)覆盖掉了,这里也就是为啥有题解说一件物品被拿了两次的原因。
思路:
属于01背包问题,判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。等价于任选数组中的元素放进背包里,如果背包里的元素和数组总和的一半,说明可以分割成两个子集。其中元素的值就是物品的容量,也就是物品的价值,那么使得背包的元素和就是背包的最大价值。
dp[j]表示背包元素容量为j是,获得的最大价值dp[j]。那么如果背包容量为target, dp[target]就是装满 背包之后的重量,所以 当 dp[target] == target 的时候,背包就装满了。
dp递推公式:dp[j] = max(dp[j], dp[j - nums[i]] + nums[i])
代码:
class Solution {
public:
bool canPartition(vector<int>& nums) {
// dp[j]中的j表示背包内总和,数组元素值num[i]等价于物品价值value[i],等价于物品重量weight[i]
// 题目中说:每个数组中的元素不会超过 100,数组的大小不会超过 200
// 总和不会大于20000,背包最大只需要其中一半,所以10001大小就可以了
vector<int> dp(10001, 0);
dp[0] = 0;
int sum = 0;
for(int num : nums){
sum += num;
}
//如果和为奇数,不可能分割成两个子集,直接返回false
if(sum % 2 == 1) return false;
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]);
}
}
//如果任取数组中的元素放进容量为 sum / 2 的背包里,其最大价值也就是数组和等于背包容量,说明可以分割成两个子集
return dp[sum / 2] == sum / 2;
}
};