剑指 Offer II 102. 加减的目标值(494. 目标和)
题目:
思路:
【1】回溯的方式:
【2】动态规划的方式:
记数组的元素和为 sum,添加 - 号的元素之和为 neg,则其余添加 + 的元素之和为 sum−neg,得到的表达式的结果为
(sum−neg)−neg=sum−2*neg=target
即
neg=(sum−target)/2
所以转变成了在数组中多个元素组合累加成为neg的问题
代码展示:
动态规划的方式:
//时间5 ms击败53.59% //内存41.1 MB击败24% class Solution { public int findTargetSumWays(int[] nums, int target) { int sum = 0; for (int num : nums) { sum += num; } //根据neg=(sum−target)/2,所以sum−target必须是偶数,否则条件不成立,且neg是正整数 int diff = sum - target; if (diff < 0 || diff % 2 != 0) { return 0; } int n = nums.length, neg = diff / 2; int[][] dp = new int[n + 1][neg + 1]; dp[0][0] = 1; for (int i = 1; i <= n; i++) { int num = nums[i - 1]; for (int j = 0; j <= neg; j++) { dp[i][j] = dp[i - 1][j]; if (j >= num) { dp[i][j] += dp[i - 1][j - num]; } } } return dp[n][neg]; } } //优化空间复杂度 //由于 dp的每一行的计算只和上一行有关,因此可以使用滚动数组的方式,去掉 dp的第一个维度,将空间复杂度优化到 O(neg)。 //时间1 ms击败100% //内存39 MB击败85.20% class Solution { public int findTargetSumWays(int[] nums, int target) { int sum = 0; for (int num : nums) { sum += num; } int diff = sum - target; if (diff < 0 || diff % 2 != 0) { return 0; } int neg = diff / 2; int[] dp = new int[neg + 1]; dp[0] = 1; for (int num : nums) { for (int j = neg; j >= num; j--) { dp[j] += dp[j - num]; } } return dp[neg]; } }
回溯的方式:
//时间564 ms击败10.54% //内存39.1 MB击败73.31% //首先回溯是能解决这个问题的,但是有点不太合适,因为数组的一个元素就代表着一层 //所以数组很大的时候层数很深,代价很大 //其次,剪枝优化部分,先看例子[1,1,1],这种数据大部分都是集中在中间部分的,剪枝的情况其实不太理想 // 0 // +1 -1 // +1 -1 +1 -1 // +1 -1 +1 -1 +1 -1 +1 -1 // 3 1 1 -1 1 -1 -1 -3 class Solution { public int findTargetSumWays(int[] nums, int target) { int sum = Arrays.stream(nums).sum(); if (target > sum || target < -sum) return 0; return dfsSum(0,0,nums,target); } private int dfsSum(int index, int sum, int[] nums, int target) { if (index == nums.length) return sum == target ? 1 : 0; return dfsSum(index+1,sum+nums[index],nums,target) + dfsSum(index+1,sum-nums[index],nums,target); } }