Leetcode 494. 目标和 dp
地址 https://leetcode-cn.com/problems/target-sum/
给你一个整数数组 nums 和一个整数 target 。
向数组中的每个整数前添加 '+' 或 '-' ,然后串联起所有整数,可以构造一个 表达式 :
例如,nums = [2, 1] ,可以在 2 之前添加 '+' ,在 1 之前添加 '-' ,然后串联起来得到表达式 "+2-1" 。
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。
示例 1:
输入:nums = [1,1,1,1,1], target = 3
输出:5
解释:一共有 5 种方法让最终目标和为 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
+1 + 1 + 1 + 1 - 1 = 3
示例 2:
输入:nums = [1], target = 1
输出:1
提示:
1 <= nums.length <= 20
0 <= nums[i] <= 1000
0 <= sum(nums[i]) <= 1000
-1000 <= target <= 1000
解答
1 暴力遍历 每个元素可以有正负两个选择 O(2^20)
2 dp 由于有负数不可作为索引。 将所有计算额外加上了1000。
3 dp 通过公式推导,变成了01背包。
方案1
dfs 暴力遍历每个元素前面添加+或者-
到最后查看得到的计算结果是否等于target
复杂度比较高. 每个元素有+-两个选择,一共20个元素,那么暴力遍历完成就是O(2^(nums.length))
2^20 = 2^10 * 2^10 = 1024*1024 约等于 10^6 勉强能不超时ac
class Solution {
public:
int ans = 0;
void dfs(vector<int>& nums,int idx,int sum,int target){
if(idx == nums.size()){
if(sum == target) ans++;
return;
}
sum +=nums[idx];
dfs(nums,idx+1,sum,target);
sum -= 2*nums[idx];
dfs(nums,idx+1,sum,target);
return;
}
int findTargetSumWays(vector<int>& nums, int target) {
dfs(nums,0,0,target);
return ans;
}
};
方案2
背包问题类型
dp[x][y] 表示在数组前x个元素中,计算得出结果为y的方案数。
由于可能出现负数,负数不能作为数字索引下标。所以整个数组计算的时候 右移了20000. 因为sum(nums[i]) <= 1000; 且nums.lenth<=20 ,最坏情况全部取负号,则20*1000=20000.稍微再多一点点防止越界所以取了20030;
也就是
dp[x][y+20030]表示在数组前x个元素中,计算得出结果为y的方案数。
记忆化搜索方案
函数 solve(nums, target,n)
表示 nums中的数组经过前n个元素选择正负号后 能得到等于target的方案数
对应的数组是dp[n][target+20030].
最开始将数组全部初始化为-1 ,表示不能,如果某个时候检测该数组不为-1 ,说明已经计算过,可以直接调用结果不必重复计算
时间复杂度是 O(n*sum(abs(nums[i]))) ; i=0~n-1
int solve(const vector<int>& nums, int target, int idx) {
if (dp[idx][target+20030] != -1) return dp[idx][target+ 20030];
}
class Solution {
public:
vector<vector<int>> dp;
int solve(const vector<int>& nums, int target, int idx) {
if (idx < 0) return 0;
if (dp[idx][target+20030] != -1) return dp[idx][target+ 20030];
int val = nums[idx];
// 自顶向下搜索
// nums中的数组经过前面元素选择正负号后 当前最后一个元素等于0 能得到等于target=0的方案数, 那么肯定是2 疑问当前元素0 选择-0 +0 都可以
if (idx == 0 && target == val && val == 0) { dp[idx][target + 20030] = 2; return dp[idx][target + 20030]; }
// nums中的数组经过前面元素选择正负号后 当前最后一个元素恰好等于target 的方案数 也就是选择+号 方案数为1
if (idx == 0 && target == val) { dp[idx][target + 20030] = 1; return dp[idx][target + 20030];}
// nums中的数组经过前面元素选择正负号后 当前最后元素等于 -target 的方案数 也就是选择-号 方案数为1
if (idx == 0 && target == -val) { dp[idx][target + 20030] = 1; return dp[idx][target + 20030]; }
//如果当前不是最后一个元素 搜索当前元素选择+ 和- 能最后得到target的方案数之和就是本函数的答案 记录进数组中
dp[idx][target + 20030] = solve(nums, target - val, idx - 1) + solve(nums, target + val, idx - 1);
return dp[idx][target+ 20030];
}
int findTargetSumWays(vector<int>& nums, int target) {
dp.resize(22, vector<int>(40030, -1));
int n = nums.size()-1;
solve(nums, target,n);
return dp[n][target+ 20030];
}
};
作 者: itdef
欢迎转帖 请保持文本完整并注明出处
技术博客 http://www.cnblogs.com/itdef/
B站算法视频题解
https://space.bilibili.com/18508846
qq 151435887
gitee https://gitee.com/def/
欢迎c c++ 算法爱好者 windows驱动爱好者 服务器程序员沟通交流
如果觉得不错,欢迎点赞,你的鼓励就是我的动力
欢迎转帖 请保持文本完整并注明出处
技术博客 http://www.cnblogs.com/itdef/
B站算法视频题解
https://space.bilibili.com/18508846
qq 151435887
gitee https://gitee.com/def/
欢迎c c++ 算法爱好者 windows驱动爱好者 服务器程序员沟通交流
如果觉得不错,欢迎点赞,你的鼓励就是我的动力
![阿里打赏](https://files.cnblogs.com/files/itdef/ali2.bmp)
![微信打赏](https://files.cnblogs.com/files/itdef/weixin2.bmp)