1049. 最后一块石头的重量 II
向背包问题的转化方式:
根据题意,最终的结果就是把石头分成最接近的两堆,然后做差。所以转化为背包问题就是,往容器为sum/2的背包里面放最大重量的石头,也就是把石头价值设为重量。最终dp[sum/2]的值就是分成的两堆中比较小的堆重 ,然后sum-dp[sum/2]就是比较大的堆重,二者做差就是最终结果。
cpp
class Solution {
public:
int lastStoneWeightII(vector<int>& stones) {
vector<int> dp(15001,0);
int sum=0;
for(int i:stones) sum+=i;
int target = sum/2;
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. 目标和
转化成背包问题的思路:
本题可化为划分两个子集的问题,left为加号集总和,right为减号集总和,求其中一个集合的放物品方式即可得到结果(因为一个集合固定了,另外一个集合也是固定的);
两个公式:left+right=sum;left-right=target;
推导出:left=(sum+target)/2;
所以left可作为背包的最大容量,然后nums中的元素即为物品,转化为了背包问题
动规五部曲-一维:
1.dp[j]的含义:容量为j的背包,在i个物品下选取,装满背包有多少种方式;
2.递推公式:dp[j]+=dp[j-nums[i]];
解释:dp[j] 的含义是前 i-1 个物品装满容量 j 的方式数量;dp[j-nums[i]] 是前 i-1 个物品装满容量 j-nums[i] 的方式数量。把dp[j]拆分为两种情况
(1) 选择当前物品 nums[i] ,那么这些能够装满 j-nums[i] 的方案,每一种都可以再加上当前物品,变成一种装满容量 j 的新方案。所以当前 dp[j] 需要加上 dp[j-nums[i]] 这一部分。
(2) 不选择当前物品nums[i],前面物品已经能够装满 j 的方案数-dp[j](上一循环的)
因此最终 dp[j] 就等于"不选当前物品的就能装满j的方案数"加上"选择当前物品能装满j的方案数"。
3.初始化方式:只需dp[0]=1,其余为0,方便后面容量进行递推,例:有一个数字1,容量为1,所以有一种方法,按照递推公式dp[1]+=dp[0],所以需要dp[0]=1,否则dp所有元素都是0了。
4.遍历顺序:和0-1背包问题一样,为了避免重复选取物品和能够放入多个物品,需要先物品后背包+倒序遍历
cpp
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int target) {
int sum = 0;
for(int i:nums) sum+=i;
if (abs(target) > sum) return 0;
if((sum+target) % 2 !=0) return 0;
int rongliang = (sum+target)/2;
vector<int> dp(rongliang+1,0);
dp[0]=1;
for(int i=0;i<nums.size();i++){
for(int j=rongliang;j>=nums[i];j--){
dp[j] += dp[j-nums[i]];
}
}
return dp[rongliang];
}
};