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.
Tips:
用简单的DFS可以做,类似Expression Add Operators,可以进行优化,用一个sum数组记录后面数的和,如果这个和小于target - 当前和的绝对值,则可以return。复杂度是O(2^n).
也可以用DP,非常快。(没看)
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
Codes:
DFS:
public class Solution {
int res = 0;
public int findTargetSumWays(int[] nums, int S) {
if (nums == null || nums.length == 0) return 0;
int[] sums = new int[nums.length];
sums[nums.length - 1] = nums[nums.length - 1];
for (int i = nums.length - 2; i >= 0; i--) {
sums[i] = sums[i + 1] + nums[i];
}
helper(nums, S, 0, 0, sums);
return res;
}
private void helper(int[] nums, int S, int pos, long eval, int[] sums) {
if (pos == nums.length) {
if (eval == S) res++;
return;
}
if (sums[pos] < Math.abs(S - eval)) return;
helper(nums, S, pos + 1, eval + nums[pos], sums);
helper(nums, S, pos + 1, eval - nums[pos], sums);
}
}
DP:
public int findTargetSumWays(int[] nums, int s) {
int sum = 0;
for (int n : nums)
sum += n;
return sum < s || (s + sum) % 2 > 0 ? 0 : subsetSum(nums, (s + sum) >>> 1);
}
public int subsetSum(int[] nums, int s) {
int[] dp = new int[s + 1];
dp[0] = 1;
for (int n : nums)
for (int i = s; i >= n; i--)
dp[i] += dp[i - n];
return dp[s];
}