494. 目标和
难度:中等
给你一个整数数组 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
代码
class Solution {
public:
/*
思路:假设有解,其中前边填‘+’号的元素的和为x,nums所有元素的总和为sum,则前边填‘-’号的元素数量有sum-x个,
则target = x-(sum-x)--->x = (target+sum)/2,因为x代表元素个数,所以必须是整数-->当(target+sum)%2 == 1时返回false(0)
##因为target和sum都是定值,所以x(前边填‘+’号的元素的和)也是定值,
所以就可以转化为背包问题:从这些数中能选出多少种和为x的方法
开一个f[nums.size()+1][x + 1]的数组,f[i][j]表示前i个元素能凑齐j的个数,
*/
/*
此题也是可以用滚动数组将空间复杂度从n*bigPage-->bigPage,但这题只要是考解题思路,这样写易于读者思考
*/
int findTargetSumWays(vector<int>& nums, int target) {
int sum = 0;
int n = nums.size();
int i,j;
for(i=0;i<n;i++){
sum+=nums[i];
}
if( (target+sum)%2 )return 0;
if(abs(target) > sum )return 0; //这一步是防止target为负数,且-target>sum,这种情况也是没有结果的
int bigPage = (sum+target)/2;
vector<vector<int>> f(n+1,vector<int>(bigPage+1));
f[0][0] = 1; //前0个元素凑齐0的次数为1
for(i=1;i<=n;i++){
for(j=0;j<=bigPage;j++){
f[i][j] = f[i-1][j]; //假设nums[i]前边加的‘-’号,这里就等于f[i-1][j]
if(j - nums[i-1]>=0){ 假设nums[i]前边加的‘+’号,则要在前i-1个元素中找f[i-1][j-nums[i-1]]的值,且需要判断下标越界
f[i][j] += f[i-1][j-nums[i-1]];
}
}
}
return f[n][bigPage];
}
};
执行结果:
通过
执行用时:
16 ms, 在所有 C++ 提交中击败了48.16%的用户
内存消耗:
12.1 MB, 在所有 C++ 提交中击败了13.82%的用户
通过测试用例:
139 / 139