给定一个非负整数数组,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 位整数存下。
方法一:DFS暴力搜索
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int S) {
int N=nums.size();
int sum=0,result=0;
DFS(nums,0,N,S,sum,result);
return result;
}
void DFS(vector<int>& nums,int index,int N,int S,int sum,int &result){
if(index==N){
if(sum==S){
result++;
}
return;
}
DFS(nums,index+1,N,S,sum+nums[index],result);
DFS(nums,index+1,N,S,sum-nums[index],result);
return;
}
};
方法二:动态规划
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int S) {
//第一步将所有元素进行求和
long long sum = 0;
for(int num : nums){
sum += num;
}
//sum代表的含义是选择所有物品,[+,+,+...+],因为所有物品都是加号
//S代表的所有物品求和去除某些物品的两倍,[+,+,-,-,+....-],负号的物品不但不选还需要重背包中取出同样重量的物品
//sum + S代表的是S中选择的物品选择两次,(S中负号物品与sum中对应的正号物品抵消,剩下的正号物品选择了两次)
long long myS = sum + S;
if (sum < S || myS % 2 == 1){//剪枝
return 0;
}
myS = myS / 2;//现在就是需要确定总重量为myS的情况数
vector<int> dp(myS + 1,0);//dp[i]代表的是凑出重量为i的情况数
dp[0] = 1;
//进行动态规划
for(int num : nums){
for(int i = myS; i >= num; --i){
dp[i] += dp[i-num];
}
}
return dp[myS];
}
};