Files
leetcode-master/problems/0494.目标和.md
youngyangyang04 560b852b53 Update
2020-11-26 10:07:01 +08:00

117 lines
4.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 这道题小细节很多
// 转为01 背包 思路不好想啊
// dp数组难在如何初始化
// dp 数组 通常比较长
如果跟着「代码随想录」一起学过[回溯算法系列](https://mp.weixin.qq.com/s/r73thpBnK1tXndFDtlsdCQ)的录友,看到这道题,应该有一种直觉,就是感觉好像回溯法可以爆搜出来。
事实确实如此,下面我也会给出响应的代码,只不过会超时,哈哈。
这道题目咋眼一看和动态规划背包啥的也没啥关系。
本题要如何是表达式结果为target
既然为target那么就一定有 left组合 - right组合 = target中的left 和right一定是固定大小的因为left + right要等于sum而sum是固定的。
公式来了, left - (sum - left) = target -> left = (target + sum)/2 。
target是固定的sum是固定的left就可以求出来。
此时问题就是在集合nums中找出和为left的组合。
在回溯算法系列中,一起学过这道题目[回溯算法39. 组合总和](https://mp.weixin.qq.com/s/FLg8G6EjVcxBjwCbzpACPw)的录友应该感觉很熟悉,这不就是组合总和问题么?
此时可以套组合总和的回溯法代码,几乎不用改动。
当然,也可以转变成序列区间选+ 或者 -,使用回溯法,那就是另一个解法。
但无论哪种回溯法时间复杂度都是是O(2^n)级别,**所以最后超时了**。
我也把代码给出来吧,大家可以了解一下,回溯的解法,以下是本题转变为组合总和问题的回溯法代码:
```
class Solution {
private:
vector<vector<int>> result;
vector<int> path;
void backtracking(vector<int>& candidates, int target, int sum, int startIndex) {
if (sum == target) {
result.push_back(path);
}
// 如果 sum + candidates[i] > target 就终止遍历
for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i++) {
sum += candidates[i];
path.push_back(candidates[i]);
backtracking(candidates, target, sum, i + 1);
sum -= candidates[i];
path.pop_back();
}
}
public:
int findTargetSumWays(vector<int>& nums, int S) {
int sum = 0;
for (int i = 0; i < nums.size(); i++) sum += nums[i];
if (S > sum) return 0; // 此时没有方案
if ((S + sum) % 2) return 0; // 此时没有方案两个int相加的时候要各位小心数值溢出的问题
int bagSize = (S + sum) / 2; // 转变为组合总和问题bagsize就是要求的和
// 以下为回溯法代码
result.clear();
path.clear();
sort(nums.begin(), nums.end()); // 需要排序
backtracking(nums, bagSize, 0, 0);
return result.size();
}
};
```
## 动态规划
使用背包要明确dp[i]表示的是什么i表示的又是什么
填满i包括i这么大容积的包有dp[i]种方法。
```
// 时间复杂度O(n^2)
// 空间复杂度可以说是O(n)也可以说是O(1),因为每次申请的辅助数组的大小是一个常数
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int S) {
int sum = 0;
for (int i = 0; i < nums.size(); i++) sum += nums[i];
if (S > sum) return 0; // 此时没有方案
if ((S + sum) % 2) return 0; // 此时没有方案两个int相加的时候要各位小心数值溢出的问题
int bagSize = (S + sum) / 2;
int dp[1001] = {1};
for (int i = 0; i < nums.size(); i++) {
for (int j = bagSize; j >= nums[i]; j--) {
if (j - nums[i] >= 0) dp[j] += dp[j - nums[i]];
}
}
return dp[bagSize];
}
};
```
dp数组中的数值变化从[0 - 4]
```
1 1 0 0 0
1 2 1 0 0
1 3 3 1 0
1 4 6 4 1
1 5 10 10 5
```
# 总结
此时 大家应该不仅想起,我们之前讲过的[回溯算法39. 组合总和](https://mp.weixin.qq.com/s/FLg8G6EjVcxBjwCbzpACPw)是不是应该也可以用dp来做啊
是的如果仅仅是求个数的话就可以用dp但[回溯算法39. 组合总和](https://mp.weixin.qq.com/s/FLg8G6EjVcxBjwCbzpACPw)要求的是把所有组合列出来,还是要使用回溯法爆搜的。