原题链接在这里: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.
题解:
List some examples. e.g. 1,1,1,1,1.
If all of them are positive, it is 5, all negitative, it is -5. Beyond [-5,5], it can't happen, thus, the ways count is 0.
For this sum question, let dp[i] denotes the sum up to i, the count of ways.
Iterate each num in nums. For num, it could be + or -. say first 1. Then it could -1 or 1.
The ways to -1 is 1, ways to 1 is 1. It is because dp[0] accumlate to dp[1] and dp[-1].
递推时, 上个可能结果或加或减当前num得到新的结果, 不同ways的数目在新结果下累计. 只有对应count大于0时才可能是上个可能结果, because it would not be out of index.
起始值dp[0 + sum] = 1. 可能结果为0的不同ways数目是1. sum is offset. dp[0] means sum up to -sum.
Time Complexity: O(sum * n). sum是nums所有num的和. n = nums.length
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 }
Method 2:
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.
update新的dp时会用到上一轮前面的值,所以要从后往前更新. 这样更新时保证用到的都是上轮的值.
Time Complexity: O(sum*nums.length). sum是nums所有num的和.
Space: O(sum).
AC Java:
class Solution { public int findTargetSumWays(int[] nums, int S) { int sum = 0; for(int num : nums){ sum += num; } if(S < -sum || S > sum || (S+sum)%2 != 0){ return 0; } return subSum(nums, (S+sum)/2); } private int subSum(int [] nums, int target){ int [] dp = new int[target+1]; dp[0] = 1; for(int num : nums){ for(int i = target; i>=num; i--){ dp[i] += dp[i-num]; } } return dp[target]; } }