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: Math + DP O(sum * n); O(sum)
set = {a,b,c,d} 如果 a+b -c-d = S,那么split positive and negative 就有a+b = S + c + d, 就有 a+b+c+d = S + 2(c + d), if total = a+b+c+d, then c+d = (total - S)/2 where total - S should be even, then we just need to find the number of subsets with target sum = (total - S) / 2.
/**
* Math + DP O(sum * n); O(sum)
* 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
* 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
*/
public int findTargetSumWays(int[] nums, int s) {
if (nums == null || nums.length == 0) {
return 0;
}
int sum = 0;
for (int num : nums) {
sum += num;
}
return s > sum || s < -sum || (s + sum) % 2 == 1 ? 0 : subsetSum(nums, (s + sum) / 2);
}
private int subsetSum(int[] nums, int s) {
int[] dp = new int[s + 1];
dp[0] = 1;
for (int num : nums) {
// for (int i = num; i <= s; i++) { //这么写就不对,不知道为什么,应该是一样的啊
//应该是这里的dp[i]只能从后往前算,因为重复元素也是有序的,从左到右排列,不能认为是简单的组合
// dp[i] += dp[i - num];
// }
for (int i = s; i >= num; i--) {
dp[i] += dp[i - num];
}
}
return dp[s];
}