题目描述:
方法1:回溯算法
方法2:动态规划
sum是固定的,target也是固定的,有式子left + right = sum,left - right = target,设left为背包容量,装满left背包的策略总数即为题解,left = (target + sum) / 2
初始dp[i][j]:使用下标为[0, i]的nums[i]能够凑满j(包括j)这么大容量的包,有dp[i][j]种方法
比如示例1,left为4,做题时多加一行,不然要分情况讨论,见第三个代码块
二维
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int target) {
int sum = 0;
for(int i=0;i<nums.size();i++) sum += nums[i];
if (sum < abs(target) || (sum + target) % 2 != 0) return 0;//left一定要是偶数
int left = (sum + target)/2;
vector<vector<int>>dp(nums.size(),vector<int>(left+1,0));
//初始化第一行dp数组
if(nums[0] <= left) dp[0][nums[0]] = 1;
dp[0][0] = 1;
for(int i=1;i<nums.size();i++){
for(int j=0;j<left+1;j++){
if(j>=nums[i]) dp[i][j] = dp[i-1][j] + dp[i-1][j-nums[i]];
else dp[i][j] = dp[i-1][j];
}
}
return dp[nums.size()-1][left];
}
};
无法通过所有测试样例
原因:第一行数组初始化错误,nums[0]为0时,dp[0][0]为2
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int target) {
int sum = 0;
for(int i=0;i<nums.size();i++) sum += nums[i];
if (sum < abs(target) || (sum + target) % 2 != 0) return 0;//left一定要是偶数
int left = (sum + target)/2;
vector<vector<int>>dp(nums.size(),vector<int>(left+1,0));
//初始化第一行dp数组
if(nums[0] == 0) dp[0][0] =2;//不取加取
else{
if(nums[0] <= left) dp[0][nums[0]] = 1;
dp[0][0] = 1;//不取nums[0]
}
for(int i=1;i<nums.size();i++){
for(int j=0;j<left+1;j++){
if(j>=nums[i]) dp[i][j] = dp[i-1][j] + dp[i-1][j-nums[i]];//不取和取
else dp[i][j] = dp[i-1][j];//不取
}
}
return dp[nums.size()-1][left];
}
};
为了避免分别讨论初始化第一行的不同情况,dp数组多加一行(类似于链表头节点)
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int target) {
int sum = 0;
for(int i=0;i<nums.size();i++) sum += nums[i];
if (sum < abs(target) || (sum + target) % 2 != 0) return 0;//left一定要是偶数
int left = (sum + target)/2;
vector<vector<int>>dp(nums.size()+1,vector<int>(left+1,0));
//初始化第一行dp数组
dp[0][0] = 1;
for(int i=1;i<nums.size()+1;i++){
for(int j=0;j<left+1;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()][left];
}
};
优化:使用滚动数组(因为二维数组中只会用到左上角)
dp[j] 表示:填满j(包括j)这么大容积的包,有dp[j]种方法
dp[j] += dp[j - nums[i]]
初始化的时候dp[0] 初始化为1,因为dp[0]是在公式中一切递推结果的起源,如果dp[0]是0的话,递推结果将都是0
一维,从后往前遍历,完全背包为从前往后
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int target) {
int sum = 0;
for(int i=0;i<nums.size();i++) sum += nums[i];
if (sum < abs(target) || (sum + target) % 2 != 0) return 0;//left一定要是偶数
int left = (sum + target)/2;
vector<int>dp(left+1,0);
dp[0] = 1;
for(int i=0;i<nums.size();i++){
for(int j=left;j>=0;j--){
if(j>=nums[i])dp[j] = dp[j] + dp[j-nums[i]];
}
}
return dp[left];
}
};