题目描述
给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
示例 1:
输入:nums = [1,5,11,5]
输出:true
解释:数组可以分割成 [1, 5, 5] 和 [11] 。
示例 2:
输入:nums = [1,2,3,5]
输出:false
解释:数组不能分割成两个元素和相等的子集。
为什么可以转化为01背包?
物品选择方式只有两种要么选择,要么不选择,这些数字可以看做是背包中的物品。与01背包不同的是,01背包的容量是不超过某个数,而本题目是等于某个数。
f[j] 表示和为j 的 nums 是否存在。既然是两个等和子集,那么该和一定为偶数,如果sum等于奇数,那么不存在。最后返回的是f[sum/2]。
f[j]=f[j] || f[j-nums[i]]; 的理解:
- 不选择 nums[i],如果在 [0, i - 1] 这个子区间内已经有一部分元素,使得它们的和为 j ,那么 dp[i][j] = true;
- 选择 nums[i],如果在 [0, i - 1] 这个子区间内就得找到一部分元素,使得它们的和为 j - nums[i]
背包问题解法:
- 01 背包问题:
如果是 01 背包,即数组中的元素不可重复使用,外循环遍历 arrs,内循环遍历 target,且内循环倒序: - 完全背包问题:
(1)如果是完全背包,即数组中的元素可重复使用并且不考虑元素之间顺序,arrs 放在外循环(保证 arrs 按顺序),target在内循环。且内循环正序。
(2)如果组合问题需考虑元素之间的顺序,需将 target 放在外循环,将 arrs 放在内循环,且内循环正序。
链接:https://leetcode-cn.com/problems/word-break/solution/yi-tao-kuang-jia-jie-jue-bei-bao-wen-ti-kchg9/
class Solution {
public:
bool canPartition(vector<int>& nums) {
int n = nums.size();
int sum=0;
for(auto x:nums) sum+=x;
if(sum%2==1) return false;
int target = sum/2;
vector<int> f(target+1);
f[0]=true;//初始化条件可以是false,不过状态转移方程会发生变化
for(int i=0;i<n;i++)
{
for(int j=target;j>=nums[i];j--)
{
f[j]=f[j] || f[j-nums[i]];
}
}
return f[target];
}
};
目标和:
给你一个整数数组 nums 和一个整数 target 。
向数组中的每个整数前添加 ‘+’ 或 ‘-’ ,然后串联起所有整数,可以构造一个 表达式 :
例如,nums = [2, 1] ,可以在 2 之前添加 ‘+’ ,在 1 之前添加 ‘-’ ,然后串联起来得到表达式 “+2-1” 。
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。
示例 1:
输入:nums = [1,1,1,1,1], target = 3
输出:5
解释:一共有 5 种方法让最终目标和为 3 。
-1 + 1 + 1 + 1 + 1 = 3
+1 - 1 + 1 + 1 + 1 = 3
+1 + 1 - 1 + 1 + 1 = 3
+1 + 1 + 1 - 1 + 1 = 3
+1 + 1 + 1 + 1 - 1 = 3
示例 2:
输入:nums = [1], target = 1
输出:1
这些数字可以看做是01背包中的物品,与上题目不同的是,该题目中的数字可正可负。假设满足条件的组合中正数之和 x 负数之和(变成负数的数字的和,不是转换为负数之后再做和) y,x-y = target,x+y = sum,那么 x =(s+sum)/2 意味着我们只要从数组中选出几个数这几个数的和是 x 即可(代码中记为了s)。又回到了01背包的常用方式,只要找到了组合中正数之和,那么其余的数字都是负数。最后返回的是f[s]
边界条件:target>sum || (target+sum)%2==1
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int target) {
int n = nums.size();
int sum=0;
for(auto x:nums) sum+=x;
int s =(target+sum)/2;
if(target>sum || (target+sum)%2==1) return false;
vector<int> f(s+1);
f[0]=1;
for(int i=0;i<n;i++)
{
for(int j=s;j>=nums[i];j--)
{
f[j]=f[j]+f[j-nums[i]];
}
}
return f[s];
}
};