[LeetCode] 494. Target Sum
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.
Thought process:
DFS exhaust all possible combinations.
Solution 1 (recursion):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public class Solution { public int findTargetSumWays(int[] nums, int S) { return dfs(nums, S, 0, 0); } private int dfs(int[] nums, int S, int sum, int index) { if (index == nums.length) { return S == sum ? 1 : 0; } int plus = dfs(nums, S, sum + nums[index], index + 1); int minus = dfs(nums, S, sum - nums[index], index + 1); return plus + minus; } } |
Time complexity: O(2^n).
The above solution makes many redundant calls to the same dfs(index, sum). I can use a 2D array to memoize the results and query them before making recursive calls. Since the sum of elements will not exceed 1000, the range of sums is [-1000, 1000]. So there are 2001 numbers in total.
Solution 2 (recursion with memoization);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | class Solution { public int findTargetSumWays(int[] nums, int S) { int[][] memo = new int[nums.length][2001]; for (int i = 0; i < memo.length; i++) { Arrays.fill(memo[i], Integer.MIN_VALUE); } return findTargetSumWays(nums, S, 0, 0, memo); } private int findTargetSumWays(int[] nums, int S, int index, int sum, int[][] memo) { if (index == nums.length) { if (sum == S) { return 1; } return 0; } int column = sum + 1000; if (memo[index][column] != Integer.MIN_VALUE) { return memo[index][column]; } int plus = findTargetSumWays(nums, S, index + 1, sum + nums[index], memo); int minus = findTargetSumWays(nums, S, index + 1, sum - nums[index], memo); memo[index][column] = plus + minus; return plus + minus; } } |
Time complexity:
Say the range of sums is s. The overall time complexity is O(ns).
There is also a dynamic programming solution.
- Sub-problem: find out how many ways to assign symbols to a sub-array of nums to make the sum equal to a (a < S).
- Function: f[i][j] = f[i - 1][j - nums[i]] + f[i - 1][j + nums[i]].
- Initialization: f[0][j] (where j = nums[j]) = 1.
- Answer: f[nums.length][S].
Solution 3 (DP):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | class Solution { public int findTargetSumWays(int[] nums, int S) { if (nums.length == 0) { return 0; } int[] f = new int[2001]; f[nums[0] + 1000] = 1; f[- nums[0] + 1000] += 1; for (int i = 1; i < nums.length; i++) { int[] next = new int[2001]; for (int sum = -1000; sum <= 1000; sum++) { if (f[sum + 1000] > 0) { next[sum + nums[i] + 1000] += f[sum + 1000]; next[sum - nums[i] + 1000] += f[sum + 1000]; } } f = next; } return S >= -1000 && S <= 1000 ? f[S + 1000] : 0; } } |
Time complexity: O(ns).
Comments
Post a Comment