LeetCode Python/Java/C++/JS > Dynamic Programming > 494. Target Sum > Solved in C#, Python, C++, Java, JavaScript, Go, Ruby > GitHub or Repost
LeetCode link: 494. Target Sum, difficulty: Medium.
You are given an integer array nums and an integer target.
You want to build an expression out of nums by adding one of the symbols + and - before each integer in nums and then concatenate all the integers.
- For example, if
nums = [2, 1], you can add a+before 2 and a-before1and concatenate them to build the expression+2-1.
Return the number of different expressions that you can build, which evaluates to target.
Example 1:
Input: nums = [1,1,1,1,1], target = 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
Example 2:
Input: nums = [1], target = 1
Output: 1
Constraints:
1 <= nums.length <= 200 <= nums[i] <= 10000 <= sum(nums[i]) <= 1000-1000 <= target <= 1000
Intuition
This problem is quite difficult if you have not solved similar problems before. So before you start working on this question, it is recommended that you first work on another relatively simple question 416. Partition Equal Subset Sum that is similar to this one.
Pattern of "Dynamic Programming"
"Dynamic Programming" requires the use of the dp array to store the results. The value of dp[i][j] can be converted from its previous (or multiple) values through a formula. Therefore, the value of dp[i][j] is derived step by step, and it is related to the previous dp record value.
"Dynamic programming" is divided into five steps
- Determine the meaning of each value of the array
dp. - Initialize the value of the array
dp. - Fill in the
dpgrid data in order according to an example. - Based on the
dpgrid data, derive the recursive formula. - Write a program and print the
dparray. If it is not as expected, adjust it.
Detailed description of these five steps
- Determine the meaning of each value of the array
dp.- First determine whether
dpis a one-dimensional array or a two-dimensional array. Aone-dimensional rolling arraymeans that the values of the array are overwritten at each iteration. Most of the time, usingone-dimensional rolling arrayinstead oftwo-dimensional arraycan simplify the code; but for some problems, such as operating "two swappable arrays", for the sake of ease of understanding, it is better to usetwo-dimensional array. - Try to use the meaning of the
return valuerequired by the problem as the meaning ofdp[i](one-dimensional) ordp[i][j](two-dimensional). It works about 60% of the time. If it doesn't work, try other meanings. - Try to save more information in the design. Repeated information only needs to be saved once in a
dp[i]. - Use simplified meanings. If the problem can be solved with
boolean value, don't usenumeric value.
- First determine whether
- Initialize the value of the array
dp. The value ofdpinvolves two levels:- The length of
dp. Usually:condition array length plus 1orcondition array length. - The value of
dp[i]ordp[i][j].dp[0]ordp[0][0]sometimes requires special treatment.
- The length of
- Fill in the
dpgrid data in order according to an example.- The "recursive formula" is the core of the "dynamic programming" algorithm. But the "recursive formula" is obscure. If you want to get it, you need to make a table and use data to inspire yourself.
- If the original example is not good enough, you need to redesign one yourself.
- According to the example, fill in the
dpgrid data "in order", which is very important because it determines the traversal order of the code. - Most of the time, from left to right, from top to bottom. But sometimes it is necessary to traverse from right to left, from bottom to top, from the middle to the right (or left), such as the "palindrome" problems. Sometimes, it is necessary to traverse a line twice, first forward and then backward.
- When the order is determined correctly, the starting point is determined. Starting from the starting point, fill in the
dpgrid data "in order". This order is also the order in which the program processes. - In this process, you will get inspiration to write a "recursive formula". If you can already derive the formula, you do not need to complete the grid.
- Based on the
dpgrid data, derive the recursive formula.- There are three special positions to pay attention to:
dp[i - 1][j - 1],dp[i - 1][j]anddp[i][j - 1], the currentdp[i][j]often depends on them. - When operating "two swappable arrays", due to symmetry, we may need to use
dp[i - 1][j]anddp[i][j - 1]at the same time.
- There are three special positions to pay attention to:
- Write a program and print the
dparray. If it is not as expected, adjust it.- Focus on analyzing those values that are not as expected.
After reading the above, do you feel that "dynamic programming" is not that difficult? Try to solve this problem. 🤗
Step-by-Step Solution
- Determine the meaning of the
dp[j]dp[j]represents that by using the firstinums, the number of different expressions that you can build, which evaluates toj.dp[j]is an integer.
Determine the
dparray's initial value- Use an example. We didn't use the
Example 1: Input: nums = [1,1,1,1,1], target = 3because it is too special and is not a good example for deriving a formula. - I made up an example:
nums = [1,2,1,2], target = 4. - First, determine the
sizeof the knapsack.- The
targetvalue may be very small, such as0, so it alone cannot determine thesizeof the knapsack. - The sum of
numsshould also be taken into account to fully cover all knapsack sizes. targetmay be negative, but considering that+and-are added tonumarbitrarily, thedp[j]should be symmetrical around0. So the result of negativetargetdp[target]is equal todp[abs(target)].- So the
sizeof the knapsack can bemax(sum(nums), target) + 1.
- The
Second, determine what are the
items. Theitemsare thenumsin this problem.So after initialization, the 'dp' array would be: # 0 1 2 3 4 5 6 # 1 0 0 0 0 0 0 # dp # 1 # 2 # 1 # 2dp[0]is set to1, indicating that an empty knapsack can be achieved by not using anynums. In addition, it is used as the starting value, and the subsequentdp[j]will depend on it. If it is0, all values ofdp[j]will be0.dp[j] = 0 (j != 0), indicating that it is impossible to getjwith nonums.
- Use an example. We didn't use the
According to an example, fill in the
dpgrid data "in order".1. Use the first num '1'. # 0 1 2 3 4 5 6 # 1 0 0 0 0 0 0 # 1 0 1 0 0 0 0 0 # dp # 2 # 1 # 22. Use the second num '2'. # 0 1 2 3 4 5 6 # 1 0 0 0 0 0 0 # 1 0 1 0 0 0 0 0 # 2 0 1 0 1 0 0 0 # 1 # 23. Use the third num '1'. # 0 1 2 3 4 5 6 # 1 0 0 0 0 0 0 # 1 0 1 0 0 0 0 0 # 2 0 1 0 1 0 0 0 # 1 2 0 2 0 1 0 0 # 24. Use the fourth num '2'. # 0 1 2 3 4 5 6 # 1 0 0 0 0 0 0 # 1 0 1 0 0 0 0 0 # 2 0 1 0 1 0 0 0 # 1 2 0 2 0 1 0 0 # 2 4 0 3 0 2 0 1 # dpAccording to the
dpgrid data, derive the "recursive formula".dp[j] = dp[abs(j - nums[i])] + dp[j + nums[i]]- If
j < nums[i],dp[j - nums[i]]will raisearray index out of rangeexception. So we use thedp[abs(j - num)]which is equal to it, because thedp[j]are symmetrical around0, such asdp[-j]equals todp[j](-jis an imaginary index). - When
j + nums[i] >= dp.length,dp[j + nums[i]]must be0to prevent interference.
- If
Write a program and print the
dparray. If it is not as expected, adjust it.
Complexity
Time complexity
O(n * sum)
Space complexity
O(n * sum)
C# #
public class Solution
{
public int FindTargetSumWays(int[] nums, int target)
{
target = Math.Abs(target);
var dp = new int[Math.Max(nums.Sum(), target) + 1];
dp[0] = 1;
foreach (var num in nums)
{
var dc = (int[])dp.Clone();
for (var j = 0; j < dp.Length; j++)
{
dp[j] = dc[Math.Abs(j - num)] + (j + num < dp.Length ? dc[j + num] : 0);
}
}
return dp[target];
}
}
Python #
class Solution:
def findTargetSumWays(self, nums: List[int], target: int) -> int:
target = abs(target)
dp = [0] * (max(sum(nums), target) + 1)
dp[0] = 1
for num in nums:
dc = dp.copy()
for j in range(len(dp)):
dp[j] = dc[abs(j - num)] + (dc[j + num] if j + num < len(dp) else 0)
return dp[target]
C++ #
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int target) {
auto sum = reduce(nums.begin(), nums.end());
target = abs(target);
auto dp = vector<int>(max(sum, target) + 1);
dp[0] = 1;
for (auto num : nums) {
auto dc = dp;
for (auto j = 0; j < dp.size(); j++) {
dp[j] = dc[abs(j - num)] + (j + num < dp.size() ? dc[j + num] : 0);
}
}
return dp[target];
}
};
Java #
class Solution {
public int findTargetSumWays(int[] nums, int target) {
var sum = IntStream.of(nums).sum();
target = Math.abs(target);
var dp = new int[Math.max(sum, target) + 1];
dp[0] = 1;
for (var num : nums) {
var dc = dp.clone();
for (var j = 0; j < dp.length; j++) {
dp[j] = dc[Math.abs(j - num)] + (j + num < dp.length ? dc[j + num] : 0);
}
}
return dp[target];
}
}
JavaScript #
var findTargetSumWays = function (nums, target) {
target = Math.abs(target)
const dp = Array(Math.max(_.sum(nums), target) + 1).fill(0)
dp[0] = 1
for (const num of nums) {
const dc = [...dp]
for (let j = 0; j < dp.length; j++) {
dp[j] = dc[Math.abs(j - num)] + (j + num < dp.length ? dc[j + num] : 0)
}
}
return dp[target]
};
Go #
func findTargetSumWays(nums []int, target int) int {
sum := 0
for _, num := range nums {
sum += num
}
target = int(math.Abs(float64(target)))
dp := make([]int, max(sum, target) + 1)
dp[0] = 1
for _, num := range nums {
dc := slices.Clone(dp)
for j := 0; j < len(dp); j++ {
addition := 0
if j + num < len(dp) {
addition = dc[j + num]
}
dp[j] = dc[int(math.Abs(float64((j - num))))] + addition
}
}
return dp[target]
}
Ruby #
def find_target_sum_ways(nums, target)
target = target.abs
dp = Array.new([ nums.sum, target ].max + 1, 0)
dp[0] = 1
nums.each do |num|
dc = dp.clone
dp.each_with_index do |_, j|
dp[j] = dc[(j - num).abs] + (j + num < dp.size ? dc[j + num] : 0)
end
end
dp[target]
end