494. Target Sum
494. Target Sum
[Problem]
Array Dynamic Programming Backtracking
Intuition
- Each element in the array can either be positive (+num) or negative (-num) to the target.
- When you have such choices (decision-making), think of DP.
- And avoid repetitive calculations by storing results for subproblems.
Approach
1. Recursion [TC: O(2^n)]
- For each number choose either negative or positive.
- Each recursive call will make two calls.
- Exponential time complexity
Complexity Analysis
- Time Complexity: O(2^n)
- Each recursive call will make two calls.
- Space Complexity: O(n)
nelements in the array, hence in worst case recursion stack will havenelements
Code (Recursion)
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 {
int totalWays = 0;
public int findTargetSumWays(int[] nums, int target) {
int n = nums.length;
findWaysRec(nums, 0, 0, target);
return totalWays;
}
// Recursion => TC: O(2^n) very bad
public void findWaysRec(int[] nums, int currentIdx, int currentSum, int target){
if(currentIdx == nums.length){
if(currentSum == target){
totalWays += 1;
}
}
else{
// take negative
findWaysRec(nums, currentIdx + 1, currentSum - nums[currentIdx], target);
// take positive
findWaysRec(nums, currentIdx + 1, currentSum + nums[currentIdx], target);
}
}
}
2. Memoization [TC: O(n * totalSum)]
- DP Memoization will help in storing the values of subproblems so that we don’t need to recalculate them over and over unnecessarily.
- Take 2D
dparray to store the no. of ways for[n][possible_sum] What can be the
possible_sum?Consider
nums = [2,1,5]=>totalSum = 2+1+5 = 8Sum with positive signs = +2 + 1 + 5 = +8(highest possible sum)Sum with negative signs = -2 - 1 - 5 = -8(lowest possible sum)The range of possible sums we can get from the array is
[-8, +8]Total possible values=[-8,-7,-6...-1,0,+1,+2...+8]=17 values[8 positive values, 8 negative values and 1 zero]Total possible values=2 * 8 + 1=2 * totalSum + 1
Hence, define
dparray fordp[n][2*totalSum + 1]- Subproblem: Any cell in the dp array
dp[n][sum]says fornelements these are the no. of ways to getsum.
Handling Negative index
- We have possible sum as negative too, say
-8. - But we cannot have array with negative index, like
dp[n][-8] - To handle this, we move the entire range from by
totalSum. - OLD RANGE + totalSum = NEW RANGE
[-8,+8] + 8 = [0, 16]- Hence New Range =
[0, 16], where- index
0correponds to -8 - index
1correponds to -7 - and so on…
- index
- Code part handling this:
1 2 3 4
// handling negative indices if(dp[n][currentSum + totalSum] != Integer.MIN_VALUE){ return dp[n][currentSum + totalSum]; }
Complexity Analysis
- Time Complexity: O(n * totalSum)
- For each of the
nitems, we evaluate up to2 * totalSum + 1possible sums. - Total combinations =
n * (2 * totalSum + 1)=O(n * totalSum).
- For each of the
- Space Complexity: O(n * totalSum)
- dp array required size
dp[n][2 * totalSum + 1]. - Recursion stack depth is
O(n), but its space is dominated by the dp array.
- dp array required size
Code(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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class Solution {
public int findTargetSumWays(int[] nums, int target) {
int n = nums.length;
// memoization
int totalSum = 0;
for(int num : nums){
totalSum += num;
}
// For sum range [-8,+8] => new range => [0, 16] indices of the `dp` array
int[][] dp = new int[n][2 * totalSum + 1];
for(int[] row : dp){
Arrays.fill(row, Integer.MIN_VALUE);
}
return findWaysMemo(nums, 0, 0, target, dp, totalSum);
}
// memoization => O(n*totalSum)
public int findWaysMemo(int[] nums, int n, int currentSum, int target, int[][] dp, int totalSum){
if(n == nums.length){
if(currentSum == target){
return 1; // found 1 way to reach this target
} else{
return 0; // did not find a way
}
}
// handling negative indices `currentSum + totalSum`
// if we already calculated this subproblem before, just return its value
if(dp[n][currentSum + totalSum] != Integer.MIN_VALUE){
return dp[n][currentSum + totalSum];
}
// take positive sign
int positive = findWaysMemo(nums, n + 1, currentSum + nums[n], target, dp, totalSum);
// take negative sign
int negative = findWaysMemo(nums, n + 1, currentSum - nums[n], target, dp, totalSum);
// store total ways from both choices
dp[n][currentSum + totalSum] = positive + negative;
return dp[n][currentSum + totalSum];
}
}
This post is licensed under CC BY 4.0 by the author.