力扣-494目标和
1、题目
给你一个整数数组 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、分析
-
题目。首先看到这个题目,我们想到的是回溯法,通过穷举进行暴力找到所有组合,回溯三部曲。但是回溯往往是指数级别的时间复杂度,所以往往超时,这个题不能够选择,所以我们可以从中使用动态规划01背包问题。
-
01背包问题。首先我们得知道我们的背包容量,可能我们刚开始以为这个target是这个背包问题的容量,然后来求,但是这个求得是多个nums[i]最接近target的值,但与我们的这个题目的
不同表达式的数目
结果输出不一致。所以我们得转换思想,重新定义。首先可以来分析,在这个"+“或”-"的过程,要想得到这个target,就是其中的和部分-减部分=target,和部分+减部分=sum,所以我们可以得到和部分-(sum-和部分)=target,即和部分=(sum+target)/2。至此,我们知道sum和target是常量,也就是和部分也是一个已知的。
-
动态方程。我们通过上面可以知道和部分可以求出来,也就是我们可以从中得到的计算部分和值,设置为我们需要的背包容量size。dp[i]表示的是部分和为i时不同表达式的数目,如果背包容量size为5的话,那么当nums[i]为1的时候,就有dp[4]种不同的方法,因为是所有数量,所以应该是累加的一个过程。所以我们可以得到我们的递推公式
dp[j] += dp[j - nums[i]]
。 -
边界值。如例1,当我们dp[1]的时候,我们的这个递推公式需要dp[0]的值,我们可以试验几次,从中可以得出我们需要的dp[0]=1。然后就是使用一维dp数组求解01背包问题。
-
符合值。我们的和部分可见是(sum+target)/2得到的,所以我们的和部分应该是一个整数,因为题目所有的数都应该是整数,所以这里可以加一个判断,不符合的直接返回0。其次,我们应该还考虑到这个整数值是不是负数的情况,然后是否符合我们能够求出的值结果。
-
书写代码。
3、代码及分析
class Solution {
public int findTargetSumWays(int[] nums, int target) {
// 1.根据这题首先联想到回溯,其次想到动态规划dp,01背包问题
// 2.设和为x,减为sum-x,有sum和target,可得x=(sum+target)/2
// 3.dp[i]表示运算的 结果等于target 的 不同数量
int sum = 0;
for (int i = 0; i < nums.length; i++){
sum += nums[i];
}
if (target > sum || (sum + target) % 2 == 1) return 0;
if (target < 0 && sum < -target) return 0;
int size = (sum + target) / 2;
if (size < 0) size = -size;
int[] dp = new int[size + 1];
dp[0] = 1;
for (int i = 0; i < nums.length; i++){
for (int j = size; j >= nums[i]; j--){
dp[j] += dp[j - nums[i]];
}
}
return dp[size];
}
}