1049. 最后一块石头的重量 II
int lastStoneWeightII(vector<int>& stones) {
int sum = 0;
for (int i = 0; i < stones.size(); i++)
{
sum += stones[i];
}
int target = sum / 2;
vector<int> dp(target+1, 0);
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];
}
这道题和昨天那道题类似,由于两个相碰的石头会剩下相差的重量,因此要将整个石堆按重量尽量平分,两堆相差的重量就是碰撞后剩下的最后一块石头的重量。因此本题物品的重量为stones[i],物品的价值也为stones[i],要放进容量为sum/2的背包中,最后返回两堆差值即可!
494. 目标和
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];
}
本题要如何使表达式结果为target,
既然为target,那么就一定有 left组合 - right组合 = target。也就是将加号的放在一边,减号的也放在一边。
left + right = sum,而sum是固定的。right = sum - left
公式来了, left - (sum - left) = target 推导出 left = (target + sum)/2 。
target是固定的,sum是固定的,left就可以求出来。
此时问题就是在集合nums中找出和为left的组合。
而如何得到递归公式呢?只要搞到nums[i],凑成dp[j]就有dp[j - nums[i]] 种方法。也就是当前背包容量的组合数等于减去当前元素值大小的背包容量的组合数,因为减去之后的背包容量再加上这个元素就是目标容量了,所以组合数相同,因此,递推公式为:
dp[j] += dp[j - nums[i]]
初始化要将dp[0]初始化为1,因为一个都不取就可以得到和为0的left。
474.一和零
int findMaxForm(vector<string>& strs, int m, int n) {
vector<vector<int>> dp(m + 1, vector<int> (n + 1, 0)); // 默认初始化0
for (string str : strs) { // 遍历物品
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背包,只不过物品的重量相当于是有两个维度,一个0一个1.
dp[i][j]:最多有i个0和j个1的strs的最大子集的大小为dp[i][j]。之后分析每个遍历的元素字符串,计算每个字符串包含的0和1的数量,然后dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);也就是说dp[i][j]的大小等于dp[0的数量减去zeroNum][1的数量减去oneNum] + 1,就是加入当前字符串或者不加入当前字符串dp[i][j],取两个中更大的那一个。