494. 目标和
给定一个非负整数数组,a1, a2, …, an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。
返回可以使最终数组和为目标数 S 的所有添加符号的方法数。
示例 1:
输入: nums: [1, 1, 1, 1, 1], S: 3
输出: 5
解释:
-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
一共有5种方法让最终目标和为3。
注意:
数组的长度不会超过20,并且数组中的值全为正数(改为非负数)。
初始的数组的和不会超过1000。
保证返回的最终结果为32位整数。
分析:
1:题目中有一个小错误:题目中说数组中的数全为正数,但是测试样例数组中是存在0的,所以应该改为全书非负数。
对于存在0的情况,其实也是本题的亮点所在吧。
首先我们求出数组中所有数的总和为sum,(即所有数标记为 +)
而我们想通过 - 来使得结果和为 S
所以我们需要将总和为 taregtsum = (sum - S) / 2 的数标记为 -
因此,问题就转化为 数组中所有数的子集的和为targetsum的子集总数
若 S > sum 或 S < -sum 或 (sum - S) 为奇数时 不存在解
对于0的处理:
由于0被标记为 + 或 - 对结果没有影响,所以0是可以在2者之间任意选择的
因此我们只需先不考虑0的存在,计算出 数组中所有正数的子集的和为targetsum的子集总数ans
我们假设数组中0的个数为num0
那么,最终的结果为 ans * pow(2,num0)
AC代码:
class Solution {
public:
int ans = 0;
void getans(vector<int>& nums,int d,int n, int cursum, int targetsum)
{
if(d >= n)
{
if(cursum == targetsum) ans++;
return;
}
if(cursum > targetsum) return;
if(cursum == targetsum)
{
ans += 1;
return;
}
for(int i=d;i<n;i++)
{
if(nums[i] > 0)
{
getans(nums,i+1,n,cursum+nums[i],targetsum);
}
}
}
int findTargetSumWays(vector<int>& nums, int S) {
int sum = 0;
int n = nums.size();
int num0 = 0;
for(int i=0;i<nums.size();i++)
{
sum += nums[i];
if(nums[i] == 0) num0++;
}
if(S < -sum || S > sum || (sum - S) % 2 == 1) return 0;
int targetsum = (sum - S) / 2;
ans = 0;
getans(nums,0,n,0,targetsum);
ans *= pow(2,num0);
return ans;
}
};