Update
This commit is contained in:
@@ -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 是5,S是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,
|
||||
|
||||
* 已经有一个1(nums[i]) 的话,有 dp[4]种方法 凑成 dp[5]。
|
||||
* 已经有一个2(nums[i]) 的话,有 dp[3]种方法 凑成 dp[5]。
|
||||
* 已经有一个3(nums[i]) 的话,有 dp[2]中方法 凑成 dp[5]
|
||||
* 已经有一个4(nums[i]) 的话,有 dp[1]中方法 凑成 dp[5]
|
||||
* 已经有一个5 (nums[i])的话,有 dp[0]中方法 凑成 dp[5]
|
||||
* 已经有一个1(nums[i]) 的话,有 dp[4]种方法 凑成 容量为5的背包。
|
||||
* 已经有一个2(nums[i]) 的话,有 dp[3]种方法 凑成 容量为5的背包。
|
||||
* 已经有一个3(nums[i]) 的话,有 dp[2]中方法 凑成 容量为5的背包
|
||||
* 已经有一个4(nums[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]];
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user