原题链接在这里:https://leetcode.com/problems/target-sum/description/
题目:
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.
Note:
- The length of the given array is positive and will not exceed 20.
- The sum of elements in the given array will not exceed 1000.
- Your output answer is guaranteed to be fitted in a 32-bit integer.
题解:
DP问题. 储存到num时所有可能结果和对应的不同ways的数目. 用dp数组在储存, 最大是sum 最小是-sum. 所以开个2*sum+1的数组.
递推时, 上个可能结果或加或减当前num得到新的结果, 不同ways的数目在新结果下累计. 只有对应count大于0时才可能是上个可能结果.
起始值dp[0 + sum] = 1. 可能结果为0的不同ways数目是1. sum像个offset.
Time Complexity: O(sum*nums.length). sum是nums所有num的和.
Space: O(sum).
AC Java:
1 class Solution { 2 public int findTargetSumWays(int[] nums, int S) { 3 if(nums == null || nums.length == 0){ 4 return 0; 5 } 6 7 int sum = 0; 8 for(int num : nums){ 9 sum += num; 10 } 11 12 if(sum < S || -sum > S){ 13 return 0; 14 } 15 16 int [] dp = new int[2*sum+1]; 17 //sum相当于 offset 18 dp[0+sum] = 1; 19 for(int num : nums){ 20 int [] next = new int[2*sum+1]; 21 for(int k = 0; k<2*sum+1; k++){ 22 if(dp[k] > 0){ 23 next[k+num] += dp[k]; 24 next[k-num] += dp[k]; 25 } 26 } 27 dp = next; 28 } 29 return dp[sum + S]; 30 } 31 }
nums中一部分用的+号 相当于positive, 另一部分用的 - 号相当于negative. 分成两组.
sum(p) - sum(n) = target.
sum(p) + sum(n) + sum(p)-sum(n) = target + sum(p) + sum(n)
2*sum(p) = target + sum(nums)
相当于在nums中找有多少种subarray, subarray自身的和是(target + sum(nums))/2的问题.
subSum求解这个转化问题.
存储到当前数字得到所有结果ways数目. 为什么循环中i要从大到小呢. 这其实是dp的space compression. 跟新新的dp时会用到上一轮前面的值,所以要从后往前更新. 这样更新时保证用到的都是上轮的值.
Time Complexity: O(sum*nums.length). sum是nums所有num的和.
Space: O(sum).
AC Java:
1 class Solution { 2 public int findTargetSumWays(int[] nums, int S) { 3 if(nums == null || nums.length == 0){ 4 return 0; 5 } 6 7 int sum = 0; 8 for(int num : nums){ 9 sum += num; 10 } 11 12 if(sum < S || -sum > S || (sum+S)%2==1){ 13 return 0; 14 } 15 return subSum(nums, (sum+S)/2); 16 } 17 18 private int subSum(int [] nums, int sum){ 19 int [] dp = new int[sum+1]; 20 dp[0]=1; 21 for(int num : nums){ 22 for(int i = sum; i>=num; i--){ 23 dp[i] += dp[i-num]; 24 } 25 } 26 return dp[sum]; 27 } 28 }