给定一个非负整数数组,a1, a2, …, an, 和一个目标数,S。现在你有两个符号 +
和 -
。对于数组中的任意一个整数,你都可以从 +
或 -
中选择一个符号添加在前面。
返回可以使最终数组和为目标数 S 的所有添加符号的方法数。
示例:
输入:nums: [1, 1, 1, 1, 1], S: 3
输出:5
解释:
-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
一共有5种方法让最终目标和为3。
提示:
- 数组非空,且长度不会超过 20 。
- 初始的数组的和不会超过 1000 。
- 保证返回的最终结果能被 32 位整数存下。
回溯算法
class Solution {
public:
int res = 0;
int findTargetSumWays(vector<int>& nums, int S) {
if(nums.size() <= 0){
return 0;
}
backtrack(nums, 0, S);
return res;
}
void backtrack(vector<int>& nums, int i, int S){
if(i == nums.size()){
if(S == 0){
res++;
}
return ;
}
S = S + nums[i];
backtrack(nums, i+1, S);
S = S - nums[i];
S = S - nums[i];
backtrack(nums, i+1, S);
S = S + nums[i];
}
};
class Solution {
public:
int res = 0;
int findTargetSumWays(vector<int>& nums, int S) {
int sum = 0;
for(int i = 0; i<nums.size();i++){
sum += nums[i];
}
if(sum<S || (sum + S) % 2 == 1){
return 0;
}
return subsets(nums, (sum + S)/2);
}
// 计算nums中有几个子集的和为target
int subsets(vector<int>& nums, int target){
vector<vector<int>> dp(nums.size() + 1, vector<int>(target+1, 0));
// base case
for(int i= 0;i<=nums.size();i++){
dp[i][0] = 1;
}
for(int i = 1; i<=nums.size();i++){
for(int j = 0; j <=target; j++){
if(j >= nums[i-1]){
// 两种选择之和
dp[i][j] = dp[i-1][j] + dp[i - 1][j - nums[i-1]];
}else{
//背包的空间不足
dp[i][j] = dp[i-1][j];
}
}
}
return dp[nums.size()][target];
}
};
class Solution {
public:
int res = 0;
int findTargetSumWays(vector<int>& nums, int S) {
int sum = 0;
for(int i = 0; i<nums.size();i++){
sum += nums[i];
}
if(sum<S || (sum + S) % 2 == 1){
return 0;
}
return subsets(nums, (sum + S)/2);
}
// 计算nums中有几个子集的和为target
int subsets(vector<int>& nums, int target){
vector<int> dp(target+1, 0);
// base case
dp[0] = 1;
for(int i = 1; i<=nums.size();i++){
for(int j = target; j >=0; j--){
if(j >= nums[i-1]){
// 两种选择之和
dp[j] = dp[j] + dp[j - nums[i-1]];
}else{
//背包的空间不足
dp[j] = dp[j];
}
}
}
return dp[target];
}
};