leetcode-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
示例 2:
输入:nums = [1], target = 1
输出:1
提示:
1 <= nums.length <= 20
0 <= nums[i] <= 1000
0 <= sum(nums[i]) <= 1000
-1000 <= target <= 100
Related Topics 深度优先搜索 动态规划
👍 795 👎 0
[题目链接]
[github地址]
[相关题目]
leetcode 链接:leetcode-1049-最后一颗石子的重量ii
本站导航: 每日一题系列
[思路介绍]
思路一:递归+dps
- 递推为0,每一次只有两种运算结果,所以可以考虑像二叉树深度优先从结果倒推最后求出子节点为0的情况
class Solution {
int res = 0;
public int findTargetSumWays(int[] nums, int target) {
int n = nums.length - 1;
return backTrack(nums, target, n);
}
private int backTrack(int[] nums, int target, int n) {
if (n == 0) {
if (target - nums[0] == 0) {
res += 1;
}
if (target + nums[0] == 0) {
res += 1;
}
return res;
}
backTrack(nums, target - nums[n], n - 1);
backTrack(nums, target + nums[n], n - 1);
return res;
}
}
时间复杂度O(n*n)
思路二:递归+dps+记忆化存储
- 通过类似斐波那契数列的方式优化查找匹配流程
class Solution {
int res = 0;
Map<String, Integer> map = new HashMap<>();
public int findTargetSumWays(int[] nums, int target) {
return backTrack(nums, target, 0, 0);
}
private int backTrack( int[] nums, int target,int cur, int n) {
if (map.containsKey(cur + "+" + n)) {
return map.get(cur + "+" + n);
}
if (n == nums.length) {
map.put(cur + "+" + n, cur == target ? 1 : 0);
return map.get(cur + "+" + n);
}
int left = backTrack( nums, target,cur - nums[n], n + 1);
int right = backTrack( nums, target,cur + nums[n],n + 1);
map.put(cur + "+" + n, left+ right);
return map.get(cur + "+" + n);
}
}
时间复杂度O(n*n)
思路三:动态规划
- 根据思路一的递归方程确定dp方程
- 可变参数 n,target
- 定义一个dp[i][j] i <= n
- 最后求得dp[n][target]
- dp[0][0] = 1;
- dp[i][j] = dp[i-1][j-nums[i-1]] + dp[i-1][j+nums[i-1]]
- i的大小范围很好界定nums.length
- target 的范围界定则需要判断所有数组和了,也就是说 sum >= target >= -sum
public int findTargetSumWays(int[] nums, int target) {
int sum = 0, n = nums.length;
for (int i : nums
) {
sum += i;
}
//cornrer case
if (target > sum) {
return 0;
}
int[][] dp = new int[n + 1][2 * sum + 1];
//target可能为负数,所以为了dp方便, y轴右移sum位,划分出2sum + 1的空间
dp[0][sum] = 1;
for (int i = 1; i <= n; i++) {
for (int j = -sum; j <= sum; j++) {
if ((j - nums[i - 1]) + sum >= 0) {
dp[i][j + sum] += dp[i - 1][(j - nums[i - 1]) + sum];
}
if ((j + nums[i - 1]) + sum <= 2 * sum) {
dp[i][j + sum] += dp[i - 1][(j + nums[i - 1]) + sum];
}
}
}
return dp[n][target+sum];
}
时间复杂度O(n*n)
思路四:动态规划+问题转换
- 把所有元素变为绝对值,然后负数就变成了减法
- 数组每个值求和为sum,减掉[m]之后为target也就是说
- sum - m -m = target
- m = (sum - target)/2
- 最后就变成了只是用加法
- dp方程还是如上定义 dp[i][j] += dp[i - 1][j - temp];
public int findTargetSumWays(int[] nums, int target) {
int n = nums.length, sum = 0;
for (int i : nums
) {
sum += Math.abs(i);
}
//corner case
if (target > sum || (sum - target) % 2 != 0) {
return 0;
}
int m = (sum - target) / 2;
int[][] dp = new int[n + 1][m + 1];
//初始状态
dp[0][0] = 1;
for (int i = 1; i <= n; i++) {
int temp = nums[i - 1];
for (int j = 0; j <= m; j++) {
//存储之前结果
dp[i][j] += dp[i - 1][j];
if (j >= temp) {
dp[i][j] += dp[i - 1][j - temp];
}
}
}
return dp[n][m];
}
时间复杂度O(n*n)