题目大意:在数组中每个数前面添‘+’或‘-’,使得数组所有元素加和等于目标和,求添加符号的方法数(数组所有元素加和不超过1000)
分析:方法一:dfs。每个位置的数有正负两种情况,递归数组中的每个数,遍历到数组末尾时判断加和是否等于目标和即可。时间复杂度为O(2^n)。
方法二:动态规划。背包问题。
状态:dp[i][j + 1000]——用nums[0~i]组合成j的方法个数。由于j可能为负,而题中说总和不超过1000,所以要将j+1000防止出现负数下标
状态转移方程:dp[i][j] = dp[i - 1][j - nums[i]] + dp[i - 1][j + nums[i]] 也就是:
dp[i][j + nums[i] + 1000] += dp[i - 1][j + 1000]
dp[i][j - nums[i] + 1000] += dp[i - 1][j + 1000]
初始化:dp[0][nums[0] + 1000] = 1;dp[0][-nums[0] + 1000] += 1(因为nums[0]可能为0,那么添正/负号均可,是两种方法)
结果:dp[nums.size()-1][S+1000]
代码:
方法一:
class Solution {
int ans = 0;
public:
int findTargetSumWays(vector<int>& nums, int S) {
dfs(nums,0,0,S);
return ans;
}
void dfs(vector<int>& nums, int cur,int s,int target){
if(cur == nums.size()){
if(s == target) ans++;
return;
}
dfs(nums,cur + 1,s + nums[cur],target);
dfs(nums,cur + 1,s - nums[cur],target);
}
};
方法二:
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int S) {
vector<vector<int>> dp(nums.size(),vector<int>(2001));
dp[0][nums[0] + 1000] = 1;
dp[0][-nums[0] + 1000] += 1;
for(int i = 1;i < nums.size();i++){
for(int j = -1000;j <= 1000;j++){
if(dp[i - 1][j + 1000] > 0){
dp[i][j + nums[i] + 1000] += dp[i - 1][j + 1000];
dp[i][j - nums[i] + 1000] += dp[i - 1][j + 1000];
}
}
}
return S > 1000 ? 0 : dp[nums.size() - 1][S + 1000];
}
};
空间优化:dp[i][j]只和dp[i-1][j]有关,所以可以优化成一维数组
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int S) {
vector<int> dp(2001);
dp[nums[0] + 1000] = 1;
dp[-nums[0] + 1000] += 1;
for(int i = 1;i < nums.size();i++){
vector<int> next(2001);
for(int j = -1000;j <= 1000;j++){
if(dp[j + 1000] > 0){
next[j + nums[i] + 1000] += dp[j + 1000];
next[j - nums[i] + 1000] += dp[j + 1000];
}
}
dp = next;
}
return S > 1000 ? 0 : dp[S + 1000];
}
};