题目:416.分割等和子集
思路:只要找到集合里能够出现sum/2的子集总和,就算是可以分割成两个相同元素和子集了。
1.dp[j]含义:背包容量为j时,放进物品后,背的最大重量为dp[j]
那么如果背包容量为target, dp[target]就是装满 背包之后的重量,所以 当 dp[target] == target 的时候,背包就装满了。
2.递推公式:dp[j] = max(dp[j], dp[j - nums[j]] + nums[j])
本题,相当于背包里放入数值,那么物品i的重量是nums[i],其价值也是nums[i]。
3.初始化:如果题目给的价值都是正整数那么非0下标都初始化为0就可以了,如果题目给的价值有负数,那么非0下标就要初始化为负无穷。
4.遍历顺序:先物品后背包;背包从后到前(若从前到后则每个物品会放入多次)
5.举例推到dp数组:dp[target]里存的是最大重量
代码如下:
class Solution {
public:bool canPartition(vector<int>& nums) {int sum = 0;//dp[i]中的i表示背包内的总和//题目中说:每个数组中的元素不会超过 100,数组大小不会超过 200//总和不会大于20000,背包最大只需要其中的一半,所以10001大小就可以了vector<int> dp(10001, 0);for(int i = 0; i < nums.size(); i++) {sum += nums[i];}//库函数一步求和:int sum = accumulate(nums.begin(), nums.end(), 0);if(sum % 2 == 1) return false;int target = sum / 2;//开始01背包for(int i = 0; i < nums.size(); i++){for(int j = target; j >= nums[i]; j--) { //每一个元素一定是不可重复放入,所以大小从大到小遍历//j代表背包容量,当j>nums[i]时,j - nums[i]才会大于0,背包里才能放下它dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]); //每次都做选择,求每次的最优解}}//集合中的元素正好可以凑成总和targetif(dp[target] == target) return true;return false;}
};
题目:1049.最后一块石头的重量||
思路:将石头尽可能的分为重量总和近似相等的两堆,是两堆相撞,得到相撞所剩下的最小值
1.dp[j]含义:背包容量为j时,最大价值为dp[j]。每个石头的重量即为每个石头的价值(这一点要注意)。
如何定义dp数组大小?极端情况下所有石头一共中3000,因为本题将石头尽可能的分为重量总和近似相等的两堆,所以背包容量1500就够了
2.递推公式:dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);
3.初始化:0
4.遍历顺序
5.打印dp数组
代码如下:
class Solution {
public:int lastStoneWeightII(vector<int>& stones) {int sum = 0;for(int i = 0; i < stones.size(); i++){sum += stones[i];}vector<int> dp(1501, 0);int target = sum / 2; //target只会向下取整//开始遍历01背包for(int i = 0; i < stones.size(); i++){for(int j = target; j >= stones[i]; j--){dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);}}return sum - dp[target] - dp[target];}
};
题目:494.目标和(可以尝试一下回溯实现,不过会超时)
class Solution {
public:int findTargetSumWays(vector<int>& nums, int target) {int sum = 0;for(int i = 0; i < nums.size(); i++){sum += nums[i];}if(abs(target) > sum) return 0; //此时没有方案if((target + sum) % 2 == 1) return 0; //通过解方程可以得到此关系int bagSize = (target + sum) / 2;vector<int> dp(bagSize + 1, 0);dp[0] = 1; //初始化要注意for(int i = 0; i < nums.size(); i++){for(int j = bagSize; j >= nums[i]; j--){dp[j] += dp[j - nums[i]];}}return dp[bagSize];}
};
总结一下:
纯背包:装满这个背包的最大价值
分割等和子集:能不能装满这个背包?
最后一块石头的重量:给出背包最大重量,问最大能装多少
目标和:给一个背包,问有多少种方式能装满