题目链接:https://leetcode.cn/problems/last-stone-weight-ii/
关键点:将石头堆分为两堆重量相似的石头堆,这样相撞之后剩余的必然是最小值。
这样题目就变成了类似分割等和子集的题目,变成了一个01背包的问题。此时,重量和价值均为石头的重量。
class Solution {
public:
int lastStoneWeightII(vector& stones) {
int sum = accumulate(stones.begin(),stones.end(),0);
int weight = sum/2;
vectordp(weight+1,0);
for(int i = 0;i=stones[i];j--)
{
dp[j] = max(dp[j],dp[j-stones[i]]+stones[i]);
}
}
return sum-2*dp[weight];
}
};
没想明白题目,看了视频后豁然开朗。
题目链接:https://leetcode.cn/problems/target-sum/
关键点:可以划分为两个子集,一个是正数子集,一个是负数子集。那么有以下几个公式:
由这两个公式可以推出:
此时问题就转化成了,有多少种方法可以使得正数子集的总和是。
就可以转变成:有多少种方法可以使得背包容量为的背包装满。
要注意边界条件:
如果除不尽的话,说明没有方法。
target的绝对值如果大于总和,方法也为0
dp数组的含义:
dp[j]代表装满容量为j的背包有dp[j]种方法。
递推公式:
,(nums[i]即物品重量)
初始化:
要将dp[0]初始化为1,否则当例子为[0],target=0时,答案不正确。
class Solution {
public:
int findTargetSumWays(vector& nums, int target) {
int sum = accumulate(nums.begin(),nums.end(),0);
// 注意边界条件
if(abs(target)>sum) return 0;
// 如果(sum+target)除不尽,那么也不会有方法
if((sum+target)%2==1) return 0;
int positive_num = (sum+target)/2;
vectordp(positive_num+1,0);
dp[0] = 1;
for(int i = 0;i=nums[i];j--)
dp[j] += dp[j-nums[i]];
}
return dp[positive_num];
}
};
学习到了新的关于01背包的应用。
题目链接:https://leetcode.cn/problems/ones-and-zeroes/
本题的背包重量其实就是允许放多少个0和1。物品的重量就是其有多少个0和1。
dp数组的含义:
dp[i][j]代表第i个0和j个1的时候最大的子集长度
递推公式:
设物品i有x个0和y个1.
其中,因为要对物品进行遍历,所以要取一个最大值的比较。里的+1是因为要将物品i也放进去
初始化:
显然,dp[0][0]应为0。因为递推公式中存在取最大值的操作,所以其余数值要初始化0,这样不会影响后续的比较。
遍历顺序:
先遍历物品再遍历背包。
遍历背包的时候要倒序遍历,这样可以使得不会出现重复添加同一物品的操作。
class Solution {
public:
int findMaxForm(vector& strs, int m, int n) {
vector> dp(m + 1, vector (n + 1, 0)); // 默认初始化0
for (string str : strs)
{ // 遍历物品,并且计算物品i有多少个0和1
int oneNum = 0, zeroNum = 0;
for (char c : str)
{
if (c == '0') zeroNum++;
else oneNum++;
}
for (int i = m; i >= zeroNum; i--)
{ // 遍历背包容量且从后向前遍历!
for (int j = n; j >= oneNum; j--)
{
dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
}
}
}
return dp[m][n];
}
};
01背包的新的应用。
01背包的应用:
1、纯01背包——装满这个背包的最大价值是多少
2、是否能装满背包——分割等和子集
3、尽可能将背包装满——最后一块石头的重量II
4、装满背包有多少种方法——目标和
5、装满容量为J的背包最多用多少种物品——一和零