代码随想录算法训练营第三十三天| 01背包问题 二维 01背包问题 一维 416. 分割等和子集
01背包问题 二维
要求:
有一个背包,他只能装4KG,分别有三个物品: 1 15;3 20; 4 30 ——》需要物品价值最大
dp[i][j] 含义:
在放物品I 的时候在J背包容量下的物品最大值
递推公式:
1,不放当前物品:dp[i-1][j]
2,放当前物品:(dp[i-1][j]) ->不应该是在当前容量下,i-1的最大价值,应该是:dp[i-1][j-weight[i]] 如果i 要3KG,那么就是i-1在1KG 下的最大值
初始化:
1,容量为0时,全为0,
2,物品0 对于J来说如果容量大于,那么就是value[0],否则为0
测试代码:
// vector<int> weight = {1, 3, 4};
// vector<int> value = { 15, 20, 30 };
// int bagweight = 4;
代码:
1 // 01背包问题 2 // 要求:有一个背包,他只能装4KG,分别有三个物品: 1 15;3 20; 4 30 ——》需要物品价值最大 3 // dp[i][j] 含义:在放物品I 的时候在J背包容量下的物品最大值 4 // 5 // 递推公式: ——》疑问,如果放当前物品,但是其实不放,放下一个才是最大值,这是怎么实现的,也就是如何实现多个解,然后选择最大值? 6 // 1,不放当前物品:dp[i-1][j] 7 // 2,放当前物品:(dp[i-1][j]) ->不应该是在当前容量下,i-1的最大价值,应该是:dp[i-1][j-weight[i]] 如果i 要3KG,那么就是i-1在1KG 下的最大值 8 // 9 // 初始化: 10 // 1,容量为0时,全为0, 11 // 2,物品0 对于J来说如果容量大于,那么就是value[0],否则为0 12 // 13 // 测试代码: 14 // vector<int> weight = {1, 3, 4}; 15 // vector<int> value = { 15, 20, 30 }; 16 // int bagweight = 4; 17 // 18 int test_2_wei_bag_problem1(vector<int>& weight, vector<int> &value, int& bagweight) 19 { 20 vector<vector<int>> dp(weight.size(), vector<int>(bagweight+1, 0)); 21 22 for (int i = 0; i <= bagweight; i++) 23 { 24 if (weight[0] <= i) 25 { 26 dp[0][i] = value[0]; 27 } 28 } 29 30 for (int i = 1; i < dp.size(); i++) 31 { 32 for (int j = 0; i <= bagweight; j++) 33 { 34 if (bagweight < weight[i]) 35 { 36 dp[i][j] = dp[i - 1][j]; 37 continue; 38 } 39 40 int cur1 = dp[i - 1][j]; 41 int cur2 = value[i] + dp[i - 1][j - weight[i]]; 42 43 dp[i][j] = max(cur1, cur2); 44 } 45 } 46 47 int result = INT_MIN; 48 for (int i = 0; i < dp.size(); i++) 49 { 50 if (result < dp[i][bagweight]) 51 { 52 result = dp[i][bagweight]; 53 } 54 } 55 56 return result; 57 }
01背包问题 一维
dp[J]:
当容量为 J 的时候的最大值
递推公式:
// J : 当前容量 I:当前物品
// 1,不放当前物品:dp[J] = dp[J]
// 2,放当前物品: dp[J] = dp[J - weight[I]] + value[I]
初始化:
// 1,dp[o] = 0
遍历顺序:
// 设想当容量为1的时候放了一个A,容量为2的时候再放了物品A dp[1] = 20 dp[2] = dp[1]+20 = 40
// 如果dp[i][j]
// dp[A][1] ; dp[B][2] = dp[A][1]+B 而不是加A
// 理想的情况:dp[1] 放了物品A,那么dp[2] 的时候就不要再放物品A了
// 因为每次是针对单个容量来搞的,并不代表和dp[0]有任何联动?——》不正确
// 先看物品A, 然后让容量从高到底来做,——》这样和dp[i-1] 有关系么? ——》 有关系,可以看下面
// 可以保证A在每个容量里只放了依次
// 当下一个容量的时候,就可以用上一个物品了
代码
1 // 使用一个一维数组 来搞动态规划 2 // dp[J]:当容量为 J 的时候的最大值 3 // 递推公式: 4 // J : 当前容量 I:当前物品 5 // 1,不放当前物品:dp[J] = dp[J] 6 // 2,放当前物品: dp[J] = dp[J - weight[I]] + value[I] 7 // 8 // 初始化: 9 // 1,dp[o] = 0 10 // 遍历顺序: 11 // 设想当容量为1的时候放了一个A,容量为2的时候再放了物品A dp[1] = 20 dp[2] = dp[1]+20 = 40 12 // 如果dp[i][j] 13 // dp[A][1] ; dp[B][2] = dp[A][1]+B 而不是加A 14 // 15 // 理想的情况:dp[1] 放了物品A,那么dp[2] 的时候就不要再放物品A了 16 // 因为每次是针对单个容量来搞的,并不代表和dp[0]有任何联动?——》不正确 17 // 先看物品A, 然后让容量从高到底来做,——》这样和dp[i-1] 有关系么? ——》 有关系,可以看下面 18 // 可以保证A在每个容量里只放了依次 19 // 当下一个容量的时候,就可以用上一个物品了 20 // 21 int test_1_wei_bag_problem(vector<int>& weight, vector<int>& value, int& bagweight) 22 { 23 vector<int> dp(bagweight + 1, 0); 24 25 for (int i = 0; i < weight.size(); i++) 26 { 27 for (int j = bagweight; j >= 0; j--) 28 { 29 if (j < weight[i]) 30 { 31 continue; 32 } 33 else 34 { 35 dp[j] = max(dp[j], dp[j - weight[i]] + value[i]); 36 } 37 } 38 } 39 40 return dp[bagweight]; 41 }
416. 分割等和子集
要求:
输入:nums = [1,5,11,5] 输出:true 解释:数组可以分割成 [1, 5, 5] 和 [11] 。
难点:
难点:
1,容量是什么
2,怎么保证当前选择的,下一个不在选择
思路:
只要里面的元素可以满足 sum(nums)/2 就可以分成这样的子集
而不需要,背包是两个子集,然后这个放一个,其他的放,那么它的weight怎么弄?是想不通的
细节:
物品是 nums
容量是 sum(nums)/2 {和weight 的定义相等,就是让容量可以满足,然后是最大值}
weight value都是nums,
所以比较的函数,仍然是 max,存放的是可以满足这个情况的最大值
代码:
1 //注意:背包问题的目标是:让容量里面的价值达到最大 2 // 1,背包的容量需要最大化 3 // 2,里面的价值需要最大化 4 // 5 // 要求:只包含正整数,非空,分割成两个子集,使得这两个子集和相等 6 // 思路:思考背包问题能不能行 7 // 有N个物品,两个容量,要求这两个容量的和相等 8 // 容量: 1-(N-1)放进去 9 // 10 // 难点: 怎么做到这个容量 和下一个容量之间的 物品不一致? 11 // 12 // 数组dp[i][j] 当把当前数字I放到第J个背包中,它的和最大? 13 // 14 // 修正后的思路 : 只要有 n 个物品的和可以填满 sum(nums)/2 就可以满足要求 15 // N个物品,容量:n个物品的和,可以填满,容量是sum(nums)/2 16 // 17 // dp[n]:第N 个容量的时候 它的最大值 18 // 递推公式: 19 // 1,不放当前物品:dp[n] = dp[n] 20 // 2,放当前物品:dp[n] = value[i] + dp[n-weight[i]] 21 // 22 // 其中value weight 都为nums 23 // 24 bool canPartition(vector<int>& nums) 25 { 26 int sum_ = 0; 27 for (int num : nums) 28 { 29 sum_ += num; 30 } 31 32 if (sum_ % 2 == 1) 33 { 34 return false; 35 } 36 vector<int> dp((sum_ / 2)+1, 0);; 37 38 for (int i = 0; i < nums.size(); i++) 39 { 40 for (int j = (sum_ / 2); j >= nums[i]; j--) 41 { 42 dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]); 43 } 44 } 45 46 return dp[(sum_ / 2)] == (sum_/2); 47 }