leetcode-master/problems/周总结/20210128动规周末总结.md

142 lines
5.5 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.

# 本周小结!(动态规划系列四)
## 周一
[动态规划:目标和!](https://mp.weixin.qq.com/s/2pWmaohX75gwxvBENS-NCw)要求在数列之间加入+ 或者 -使其和为S。
所有数的总和为sum假设加法的总和为x那么可以推出x = (S + sum) / 2。
S 和 sum都是固定的那此时问题就转化为01背包问题数列中的数只能使用一次: 给你一些物品数字装满背包就是x有几种方法。
1. 确定dp数组以及下标的含义
**dp[j] 表示填满j包括j这么大容积的包有dp[i]种方法**
2. 确定递推公式
dp[i] += dp[j - nums[j]]
**注意:求装满背包有几种方法类似的题目,递推公式基本都是这样的**
3. dp数组如何初始化
dp[0] 初始化为1 dp[j]其他下标对应的数值应该初始化为0。
4. 确定遍历顺序
01背包问题一维dp的遍历nums放在外循环target在内循环且内循环倒序。
5. 举例推导dp数组
输入nums: [1, 1, 1, 1, 1], S: 3
bagSize = (S + sum) / 2 = (3 + 5) / 2 = 4
dp数组状态变化如下
![494.目标和](https://img-blog.csdnimg.cn/20210125120743274.jpg)
## 周二
这道题目[动态规划:一和零!](https://mp.weixin.qq.com/s/x-u3Dsp76DlYqtCe0xEKJw)算有点难度。
**不少同学都以为是多重背包其实这是一道标准的01背包**
这不过这个背包有两个维度一个是m 一个是n而不同长度的字符串就是不同大小的待装物品。
**所以这是一个二维01背包**
1. 确定dp数组dp table以及下标的含义
**dp[i][j]最多有i个0和j个1的strs的最大子集的大小为dp[i][j]。**
2. 确定递推公式
dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
字符串集合中的一个字符串0的数量为zeroNum1的数量为oneNum。
3. dp数组如何初始化
因为物品价值不会是负数初始为0保证递推的时候dp[i][j]不会被初始值覆盖。
4. 确定遍历顺序
01背包一定是外层for循环遍历物品内层for循环遍历背包容量且从后向前遍历
5. 举例推导dp数组
以输入:["10","0001","111001","1","0"]m = 3n = 3为例
最后dp数组的状态如下所示
![474.一和零](https://img-blog.csdnimg.cn/20210120111201512.jpg)
## 周三
此时01背包我们就讲完了正式开始完全背包。
在[动态规划:关于完全背包,你该了解这些!](https://mp.weixin.qq.com/s/akwyxlJ4TLvKcw26KB9uJw)中我们讲解了完全背包的理论基础。
其实完全背包和01背包区别就是完全背包的物品是无限数量。
递推公式也是一样的,但难点在于遍历顺序上!
完全背包的物品是可以添加多次的,所以遍历背包容量要从小到大去遍历,即:
```C++
// 先遍历物品,再遍历背包
for(int i = 0; i < weight.size(); i++) { // 遍历物品
for(int j = weight[i]; j < bagWeight ; j++) { // 遍历背包容量
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
}
```
基本网上题的题解介绍到这里就到此为止了
**那么为什么要先遍历物品,在遍历背包呢?** 灵魂拷问
其实对于纯完全背包先遍历物品再遍历背包 先遍历背包再遍历物品都是可以的我在文中[动态规划关于完全背包你该了解这些](https://mp.weixin.qq.com/s/akwyxlJ4TLvKcw26KB9uJw)也给出了详细的解释
这个细节是很多同学忽略掉的点其实也不算细节了**相信不少同学在写背包的时候两层for循环的先后循序搞不清楚靠感觉来的**。
所以理解究竟是先遍历啥后遍历啥非常重要这也体现出遍历顺序的重要性
在文中我也强调了是对纯完全背包两个for循环先后循序无所谓那么题目稍有变化可就有所谓了
## 周四
[动态规划给你一些零钱你要怎么凑](https://mp.weixin.qq.com/s/PlowDsI4WMBOzf3q80AksQ)中就是给你一堆零钱零钱个数无限为凑成amount的组合数有几种
**注意这里组合数和排列数的区别!**
看到无限零钱个数就知道是完全背包
但本题不是纯完全背包了求是否能装满背包而是求装满背包有几种方法
这里在遍历顺序上可就有说法了
* 如果求组合数就是外层for循环遍历物品内层for遍历背包
* 如果求排列数就是外层for遍历背包内层for循环遍历物品
这里同学们需要理解一波我在文中也给出了详细的解释下周我们将介绍求排列数的完全背包题目来加深对这个遍历顺序的理解
## 总结
相信通过本周的学习大家已经初步感受到遍历顺序的重要性
很多对动规理解不深入的同学都会感觉动规嘛就是把递推公式推出来其他都easy了
其实这是一种错觉或者说对动规理解的不够深入
我在动规专题开篇介绍[关于动态规划你该了解这些](https://mp.weixin.qq.com/s/ocZwfPlCWrJtVGACqFNAag)中就强调了 **递推公式仅仅是 动规五部曲里的一小部分, dp数组的定义、初始化、遍历顺序哪一点没有搞透的话即使知道递推公式遇到稍稍难一点的动规题目立刻会感觉写不出来了**
此时相信大家对动规五部曲也有更深的理解了同样也验证了Carl之前讲过的**简单题是用来学习方法论的而遇到难题才体现出方法论的重要性**