You are given a list of non-negative integers, a1, a2, ..., an, and a target, S. Now you have 2 symbols +
and -
. For each integer, you should choose one from +
and -
as its new symbol.
Find out how many ways to assign symbols to make sum of integers equal to target S.
Example 1:
Input: nums is [1, 1, 1, 1, 1], S is 3. Output: 5 Explanation: -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 There are 5 ways to assign symbols to make the sum of nums be target 3.
因为这里涉及到多种不同的选择,所以很容易联想到用dfs,代码如下:
1 class Solution { 2 public int findTargetSumWays(int[] nums, int S) { 3 int[] count = { 0 }; 4 helper(nums, 0, S, count); 5 return count[0]; 6 } 7 8 private void helper(int[] nums, int index, int S, int[] count) { 9 if (index == nums.length && S == 0) { 10 count[0]++; 11 } 12 if (index >= nums.length) return; 13 helper(nums, index + 1, S + nums[index], count); 14 helper(nums, index + 1, S - nums[index], count); 15 } 16 }
但是这种方法明显是没有优化的,所以时间比其他方法要高。简单的优化就是当我们到了i-th这个位置的时候,如果发现后面部分的值的和或者差都不可能达到target值,我们就应该放弃。
1 public class Solution { 2 public int findTargetSumWays(int[] nums, int S) { 3 if(nums == null || nums.length == 0) return 0; 4 5 int n = nums.length; 6 int[] sums = new int[n]; 7 int[] count = { 0 }; 8 sums[n - 1] = nums[n - 1]; 9 for (int i = n - 2; i >= 0; i--) { 10 sums[i] = sums[i + 1] + nums[i]; 11 } 12 helper(nums, sums, S, 0, count); 13 return count[0]; 14 } 15 public void helper(int[] nums, int[] sums, int target, int pos, int[] count){ 16 if(pos == nums.length && target == 0){ 17 count[0]++; 18 } 19 20 if (pos == nums.length) return; 21 if (sums[pos] < Math.abs(target)) return; 22 23 helper(nums, sums, target + nums[pos], pos + 1, count); 24 helper(nums, sums, target - nums[pos], pos + 1, count); 25 } 26 }
还有就是通过dp来做,解法如下:https://leetcode.com/problems/target-sum/discuss/97335/Short-Java-DP-Solution-with-Explanation
this is a classic knapsack problem
in knapsack, we decide whether we choose this element or not
in this question, we decide whether we add this element or minus it
So start with a two dimensional array dp[i][j]
which means the number of ways for first i-th
element to reach a sum j
we can easily observe that dp[i][j] = dp[i-1][j+nums[i]] + dp[i-1][j-nums[i]
,
Another part which is quite confusing is return value, here we return dp[sum+S]
, why is that?
because dp's range starts from -sum --> 0 --> +sum
so we need to add sum first, then the total starts from 0
, then we add S
Actually most of Sum problems can be treated as knapsack problem, hope it helps
1 public int findTargetSumWays(int[] nums, int S) { 2 3 int sum = 0; 4 for(int n: nums){ 5 sum += n; 6 } 7 if (S < -sum || S > sum) { return 0;} 8 9 int[][] dp = new int[nums.length + 1][ 2 * sum + 1]; 10 dp[0][0 + sum] = 1; // 0 + sum means 0, 0 means -sum, check below graph 11 for(int i = 1; i <= nums.length; i++){ 12 for(int j = 0; j < 2 * sum + 1; j++){ 13 14 if(j + nums[i - 1] < 2 * sum + 1) dp[i][j] += dp[i - 1][j + nums[i - 1]]; 15 if(j - nums[i - 1] >= 0) dp[i][j] += dp[i - 1][j - nums[i - 1]]; 16 } 17 } 18 return dp[nums.length][sum + S]; 19 }