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:
输入:nums = [1], target = 1
输出:1
提示:
1 <= nums.length <= 20
0 <= nums[i] <= 1000
0 <= sum(nums[i]) <= 1000
-1000 <= target <= 100
Related Topics 深度优先搜索 动态规划
👍 682 👎 0
2. 题解
2.1 解法1: 暴力DFS
class Solution {
int ans = 0;
public int findTargetSumWays(int[] nums, int target) {
if (nums.length == 0) {
return 0;
}
dfs(0, nums[0], nums, target);
dfs(0, -nums[0], nums, target);
return ans;
}
// index 为当前遍历到的索引, sum 当前总和
public void dfs(int index, int sum, int[] nums, int target) {
if (index >= nums.length) {
return;
}
if (index == nums.length - 1 && sum == target) {
ans++;
}
if (index + 1 < nums.length) {
dfs(index + 1, sum + nums[index + 1], nums, target);
dfs(index + 1, sum - nums[index + 1], nums, target);
}
}
}
另一种写法:
public class Solution {
int count = 0;
public int findTargetSumWays(int[] nums, int S) {
calculate(nums, 0, 0, S);
return count;
}
public void calculate(int[] nums, int i, int sum, int S) {
if (i == nums.length) {
if (sum == S)
count++;
} else {
calculate(nums, i + 1, sum + nums[i], S);
calculate(nums, i + 1, sum - nums[i], S);
}
}
}
2.2 解法2: 动态规划
- 状态数组: dp[i][j] 表示用数组中的前 i 个元素,组成和为 j 的方案数
- 递推公式: 考虑第 i 个数 nums[i],它可以被添加 + 或 -,因此状态转移方程如下:
dp[i][j] = dp[i - 1][j - nums[i]] + dp[i - 1][j + nums[i]]
- 初始化: 由于每个元素可能加也可能减, 但数组的下标没有负数, 所以需要将整下标右移 sum
设数组的所有元素和为 sum, 所以dp 第二维的长度应该为 2*sum+1, 初始化时, dp[0][sum] 相当于 0 点
dp[0][sum-nums[i]]=1, dp[0][sum+nums[i]]=1,
特别需要注意的是: nums[0]可能等于0, 此时dp[0][sum]需要初始化为2,因为加减0都得0 - 输出结果: dp[len-1][target+sum]
注意点:
- 元素初始化
- 递推时, 当左右的下标值不在范围内, 需要取 0
- 最后输出结果为dp[len - 1][target + sum]
- 需要排除target绝对值范围超过了sum的绝对值范围的情况
class Solution {
public int findTargetSumWays(int[] nums, int target) {
int len = nums.length;
int sum = 0;
// 计算总和
for (int i = 0; i < len; i++) {
sum += nums[i];
}
// target绝对值范围超过了sum的绝对值范围则无法得到
if (Math.abs(target) > Math.abs(sum)) {
return 0;
}
int t = 2 * sum + 1;
int[][] dp = new int[len][t];
// 初始化第一行
if (nums[0] == 0) {
dp[0][sum] = 2;
} else {
dp[0][sum - nums[0]] = 1;
dp[0][sum + nums[0]] = 1;
}
for (int i = 1; i < len; i++) {
for (int j = 0; j < t; j++) {
int left = j - nums[i] >= 0 ? j - nums[i] : 0;
int right = j + nums[i] < t ? j + nums[i] : 0;
dp[i][j] = dp[i - 1][left] + dp[i - 1][right];
}
}
return dp[len - 1][target + sum];
}
}