This commit is contained in:
programmercarl
2022-11-27 18:11:35 +08:00
parent 0ad33e5743
commit 2cd1ebe576
15 changed files with 193 additions and 116 deletions

View File

@@ -3,9 +3,11 @@
<img src="../pics/训练营.png" width="1000"/>
</a>
<p align="center"><strong><a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
# 动态规划:目标和!
## 494. 目标和
# 494. 目标和
[力扣题目链接](https://leetcode.cn/problems/target-sum/)
@@ -52,9 +54,9 @@
既然为target那么就一定有 left组合 - right组合 = target。
left + right等于sum而sum是固定的。
left + right = sum而sum是固定的。right = sum - left
公式来了, left - (sum - left) = target -> left = (target + sum)/2 。
公式来了, left - (sum - left) = target 推导出 left = (target + sum)/2 。
target是固定的sum是固定的left就可以求出来。
@@ -117,22 +119,26 @@ public:
假设加法的总和为x那么减法对应的总和就是sum - x。
所以我们要求的是 x - (sum - x) = S
所以我们要求的是 x - (sum - x) = target
x = (S + sum) / 2
x = (target + sum) / 2
**此时问题就转化为装满容量为x背包有几种方法**
大家看到(S + sum) / 2 应该担心计算的过程中向下取整有没有影响
这里的x就是bagSize也就是我们后面要求的背包容量
大家看到(target + sum) / 2 应该担心计算的过程中向下取整有没有影响。
这么担心就对了例如sum 是5S是2的话其实就是无解的所以
```CPP
```CPP
C++代码中输入的S 就是题目描述的 target
if ((S + sum) % 2 == 1) return 0; // 此时没有方案
```
同时如果 S的绝对值已经大于sum那么也是没有方案的。
```CPP
C++代码中输入的S 就是题目描述的 target
if (abs(S) > sum) return 0; // 此时没有方案
```
@@ -156,17 +162,15 @@ dp[j] 表示填满j包括j这么大容积的包有dp[j]种方法
有哪些来源可以推出dp[j]呢?
不考虑nums[i]的情况下填满容量为j的背包有dp[j]种方法。
那么考虑nums[i]的话只要搞到nums[i]凑成dp[j]就有dp[j - nums[i]] 种方法。
只要搞到nums[i]凑成dp[j]就有dp[j - nums[i]] 种方法。
例如dp[j]j 为5
* 已经有一个1nums[i] 的话,有 dp[4]种方法 凑成 dp[5]
* 已经有一个2nums[i] 的话,有 dp[3]种方法 凑成 dp[5]
* 已经有一个3nums[i] 的话,有 dp[2]中方法 凑成 dp[5]
* 已经有一个4nums[i] 的话,有 dp[1]中方法 凑成 dp[5]
* 已经有一个5 nums[i])的话,有 dp[0]中方法 凑成 dp[5]
* 已经有一个1nums[i] 的话,有 dp[4]种方法 凑成 容量为5的背包
* 已经有一个2nums[i] 的话,有 dp[3]种方法 凑成 容量为5的背包
* 已经有一个3nums[i] 的话,有 dp[2]中方法 凑成 容量为5的背包
* 已经有一个4nums[i] 的话,有 dp[1]中方法 凑成 容量为5的背包
* 已经有一个5 nums[i])的话,有 dp[0]中方法 凑成 容量为5的背包
那么凑整dp[5]有多少方法呢,也就是把 所有的 dp[j - nums[i]] 累加起来。
@@ -182,9 +186,19 @@ dp[j] += dp[j - nums[i]]
从递归公式可以看出在初始化的时候dp[0] 一定要初始化为1因为dp[0]是在公式中一切递推结果的起源如果dp[0]是0的话递归结果将都是0。
dp[0] = 1理论上也很好解释装满容量为0的背包有1种方法就是装0件物品。
这里有录友可能认为从dp数组定义来说 dp[0] 应该是0也有录友认为dp[0]应该是1。
dp[j]其他下标对应的数值应该初始化为0从递归公式也可以看出dp[j]要保证是0的初始值才能正确的由dp[j - nums[i]]推导出来。
其实不要硬去解释它的含义,咱就把 dp[0]的情况带入本题看看就是应该等于多少。
如果数组[0] target = 0那么 bagSize = (target + sum) / 2 = 0。 dp[0]也应该是1 也就是说给数组里的元素 0 前面无论放加法还是减法,都是 1 种方法。
所以本题我们应该初始化 dp[0] 为 1。
可能有同学想了,那 如果是 数组[0,0,0,0,0] target = 0 呢。
其实 此时最终的dp[0] = 32也就是这五个零 子集的所有组合情况但此dp[0]非彼dp[0]dp[0]能算出32其基础是因为dp[0] = 1 累加起来的。
dp[j]其他下标对应的数值也应该初始化为0从递归公式也可以看出dp[j]要保证是0的初始值才能正确的由dp[j - nums[i]]推导出来。
4. 确定遍历顺序
@@ -213,7 +227,6 @@ public:
if (abs(S) > sum) return 0; // 此时没有方案
if ((S + sum) % 2 == 1) return 0; // 此时没有方案
int bagSize = (S + sum) / 2;
if (bagsize < 0) return 0;
vector<int> dp(bagSize + 1, 0);
dp[0] = 1;
for (int i = 0; i < nums.size(); i++) {
@@ -238,7 +251,7 @@ public:
本题还是有点难度,大家也可以记住,在求装满背包有几种方法的情况下,递推公式一般为:
```
```CPP
dp[j] += dp[j - nums[i]];
```