代码随想录刷题Day43 | 1049. 最后一块石头的重量 II、494.目标和、 474.一和零
今日任务
1049.最后一块石头的重量 II
主要思想
- 本题的目的就是将石头分为尽量相等的两堆,与划分子集那题一样的思路
- 背包容量为 sum / 2
- 物品重量和价值均为 stones[i]
- dp[sum / 2] 表示背包为sum / 2时,背包所放物品的最大价值
- 1.dp[sum / 2] == sum / 2 时说明相等,那么返回0
- 2.如果不相等,那么此时dp[sum / 2] 是分得较少那一堆,返回 sum - dp[sum / 2] - dp[sum / 2]
- ps: sum可能不是偶数,说明最后肯定会剩一块,然而dp数组还是一样进行计算,sum - dp[sum / 2] 为另一堆, dp[sum / 2] 为本次分的一堆
class Solution {
public:
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]);
}
}
if ((sum % 2 == 1) || (dp[target] != target)) return sum - dp[target] - dp[target];
return 0;
}
};
494.目标和
主要思想
-
此处可以推导出包的
bagSize
是(sum + target) / 2
推导过程:假设分为的两堆的数值和分别为left和right,那么可以有公式
left + right = sum, left - right = target
两式相加再移项得出:left = (sum + target) / 2
-
dp[j]
表示:填满j
(包括j
)这么大容积的包,有dp[j]
种方法 -
递推公式:
dp[j] += dp[j - num[i]]
已经有一个1(nums[i]) 的话,有 dp[4]种方法 凑成 容量为5的背包。
已经有一个2(nums[i]) 的话,有 dp[3]种方法 凑成 容量为5的背包。
已经有一个3(nums[i]) 的话,有 dp[2]中方法 凑成 容量为5的背包
已经有一个4(nums[i]) 的话,有 dp[1]中方法 凑成 容量为5的背包
已经有一个5 (nums[i])的话,有 dp[0]中方法 凑成 容量为5的背包
-
PS:
dp[0]
初始化为1
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int target) {
// 设nums中所有数值和为sum,可以推导出
// 加号组合的和为 (sum + target) / 2
// dp[j] 表示:填满j(包括j)这么大容积的包,有dp[j]种方法
int sum = 0;
for (int i = 0; i < nums.size(); ++i) sum += nums[i];
if (abs(target) > sum) return 0;
if ((sum + target) % 2 == 1) return 0;
int bagSize = (sum + target) / 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];
}
};
494.一和零
主要思想
dp[i][j]
表示背包中最多i个0,j个1时,最大子集长度i
和j
需要同时满足才放入当前物品,所以i
和j
合起来相当于背包问题中物品的重量
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int target) {
// 设nums中所有数值和为sum,可以推导出
// 加号组合的和为 (sum + target) / 2
// dp[j] 表示:填满j(包括j)这么大容积的包,有dp[j]种方法
int sum = 0;
for (int i = 0; i < nums.size(); ++i) sum += nums[i];
if (abs(target) > sum) return 0;
if ((sum + target) % 2 == 1) return 0;
int bagSize = (sum + target) / 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];
}
};