494. Target Sum (Medium)
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.
Solution 1: Brute Force, DFS 789ms
class Solution {
void dfs(vector<int>& nums, int S, int i, int cur) {
if (i == nums.size()) {
if (cur == S) ++res;
return;
}
dfs(nums, S, i+1, cur+nums[i]);
dfs(nums, S, i+1, cur-nums[i]);
}
public:
int findTargetSumWays(vector<int>& nums, int S) {
dfs(nums, S, 0, 0);
return res;
}
private:
int res = 0;
};
Solution 2: Subset Sum, DP 9ms
The recursive solution is very slow, because its runtime is exponential
The original problem statement is equivalent to:
Find a subset of nums
that need to be positive, and the rest of them negative, such that the sum is equal to target
Let P
be the positive subset and N
be the negative subset
For example:
Given nums = [1, 2, 3, 4, 5]
and target = 3
then one possible solution is +1-2+3-4+5 = 3
Here positive subset is P = [1, 3, 5]
and negative subset is N = [2, 4]
Then let's see how this can be converted to a subset sum problem:
sum(P) - sum(N) = target
sum(P) + sum(N) + sum(P) - sum(N) = target + sum(P) + sum(N)
2 * sum(P) = target + sum(nums)
So the original problem has been converted to a subset sum problem as follows:
Find a subset P
of nums
such that sum(P) = (target + sum(nums)) / 2
Note that the above formula has proved that target + sum(nums)
must be even
We can use that fact to quickly identify inputs that do not have a solution (Thanks to @BrunoDeNadaiSarnaglia for the suggestion)
For detailed explanation on how to solve subset sum problem, you may refer to Partition Equal Subset Sum
class Solution {
int subsetSum(vector<int>& nums, int s) {
int dp[s+1] = {0}; // number of ways to add up to current number i
dp[0] = 1; // no number is positive
for (int n : nums) {
for (int i = s; i >= n; --i) {
dp[i] += dp[i-n];
}
}
return dp[s];
}
public:
int findTargetSumWays(vector<int>& nums, int S) {
int sum = accumulate(nums.begin(), nums.end(), 0);
return (sum < S || (S+sum) % 2) ? 0 : subsetSum(nums, (S+sum) >> 1); // (S+sum) & 2
}
};