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.
思路:
(1)暴力求法
使用dfs,就是一个前序遍历二叉树的实现,递归地+或-每个元素,到所有元素都遍历完成的时候,最后那个判断target是否等于零。
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int S) {
return dfs(nums, S, 0);
}
int dfs(vector<int> &nums, uint target, int left) {
if (target == 0 && left == nums.size()) return 1;
if (left >= nums.size()) return 0;
int ans = 0;
ans += dfs(nums, target - nums[left], left + 1);
ans += dfs(nums, target + nums[left], left + 1);
return ans;
}
};
(2)背包问题:
思路就是把整个集合看成两个子集,Q表示整个集合,P表示正数子集,N表示负数子集, T表示目标和,用S(X)表示集合的求和函数,集合中均为非负数,N集合是指选中这部分元素作为负数子集。
S(P) - S(N) = T
S(P) + S(N) + S(P) - S(N) = T + S(P) + S(N)
2S(P) = S(Q) + T
也就是:正数集的和的两倍 == 等于目标和 + 序列总和
所以问题就转换成了,找到一个正数集P,其和的两倍等于目标和+序列总和。。。
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int S) {
long sum = 0;
for (const int &it : nums) sum += it;
if ((S + sum) % 2 == 1 || S > sum) return 0;
S = (S + sum) / 2; //转化等效问题
vector<int> dp(S+1,0);
dp[0] =1;
//背包问题求法
for (const int &it : nums) {
for (int j = S; j >= it; j--)
dp[j] += dp[j - it];
}
return dp[S];
}
};