mirror of
https://github.com/youngyangyang04/leetcode-master.git
synced 2025-07-10 20:40:39 +08:00
Update
This commit is contained in:
@ -157,8 +157,6 @@ public:
|
||||
* 时间复杂度O(n * m) n m 分别为obstacleGrid 长度和宽度
|
||||
* 空间复杂度O(n * m)
|
||||
|
||||
至于能不能优化空间降为一维dp数组,我感觉不太行,因为要考虑障碍,如果把这些障碍压缩到一行,结果一定就不一样了。
|
||||
|
||||
## 总结
|
||||
|
||||
本题是[62.不同路径](https://mp.weixin.qq.com/s/MGgGIt4QCpFMROE9X9he_A)的障碍版,整体思路大体一致。
|
||||
|
@ -8,6 +8,8 @@
|
||||
|
||||
## 343. 整数拆分
|
||||
|
||||
题目链接:https://leetcode-cn.com/problems/integer-break/
|
||||
|
||||
给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。
|
||||
|
||||
示例 1:
|
||||
@ -51,9 +53,16 @@ dp[i]的定义讲贯彻整个解题过程,下面哪一步想不懂了,就想
|
||||
|
||||
j是从1开始遍历,拆分j的情况,在遍历j的过程中其实都计算过了。
|
||||
|
||||
那么从1遍历j,比较(i - j) * j和dp[i - j] * j 取最大的。
|
||||
**那有同学问了,j怎么就不拆分呢?**
|
||||
|
||||
j是从1开始遍历,拆分j的情况,在遍历j的过程中其实都计算过了。那么从1遍历j,比较(i - j) * j和dp[i - j] * j 取最大的。递推公式:dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j));
|
||||
|
||||
也可以这么理解,j * (i - j) 是单纯的把整数拆分为两个数相乘,而j * dp[i - j]是拆分成两个以及两个以上的个数相乘。
|
||||
|
||||
如果定义dp[i - j] * dp[j] 也是默认将一个数强制拆成4份以及4份以上了。
|
||||
|
||||
所以递推公式:dp[i] = max({dp[i], (i - j) * j, dp[i - j] * j});
|
||||
|
||||
递推公式:dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j));
|
||||
|
||||
3. dp的初始化
|
||||
|
||||
|
@ -11,7 +11,8 @@
|
||||
|
||||
> 如果哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费!
|
||||
|
||||
# 349. 两个数组的交集
|
||||
|
||||
## 349. 两个数组的交集
|
||||
|
||||
https://leetcode-cn.com/problems/intersection-of-two-arrays/
|
||||
|
||||
@ -23,7 +24,7 @@ https://leetcode-cn.com/problems/intersection-of-two-arrays/
|
||||
输出结果中的每个元素一定是唯一的。
|
||||
我们可以不考虑输出结果的顺序。
|
||||
|
||||
# 思路
|
||||
## 思路
|
||||
|
||||
这道题目,主要要学会使用一种哈希数据结构:unordered_set,这个数据结构可以解决很多类似的问题。
|
||||
|
||||
@ -31,7 +32,7 @@ https://leetcode-cn.com/problems/intersection-of-two-arrays/
|
||||
|
||||
这道题用暴力的解法时间复杂度是O(n^2),那来看看使用哈希法进一步优化。
|
||||
|
||||
那么用数组来做哈希表也是不错的选择,例如[242. 有效的字母异位词](https://mp.weixin.qq.com/s/vM6OszkM6L1Mx2Ralm9Dig),[0383.赎金信](https://mp.weixin.qq.com/s/sYZIR4dFBrw_lr3eJJnteQ)
|
||||
那么用数组来做哈希表也是不错的选择,例如[242. 有效的字母异位词](https://mp.weixin.qq.com/s/ffS8jaVFNUWyfn_8T31IdA)
|
||||
|
||||
但是要注意,**使用数组来做哈希的题目,是因为题目都限制了数值的大小。**
|
||||
|
||||
@ -52,6 +53,7 @@ std::set和std::multiset底层实现都是红黑树,std::unordered_set的底
|
||||

|
||||
|
||||
C++代码如下:
|
||||
|
||||
```C++
|
||||
class Solution {
|
||||
public:
|
||||
@ -69,6 +71,13 @@ public:
|
||||
};
|
||||
```
|
||||
|
||||
## 拓展
|
||||
|
||||
那有同学可能问了,遇到哈希问题我直接都用set不就得了,用什么数组啊。
|
||||
|
||||
直接使用set 不仅占用空间比数组大,而且速度要比数组慢,set把数值映射到key上都要做hash计算的。
|
||||
|
||||
不要小瞧 这个耗时,在数据量大的情况,差距是很明显的。
|
||||
|
||||
|
||||
## 其他语言版本
|
||||
@ -114,6 +123,9 @@ Python:
|
||||
Go:
|
||||
|
||||
|
||||
## 相关题目
|
||||
|
||||
* 350.两个数组的交集 II
|
||||
|
||||
|
||||
-----------------------
|
||||
|
@ -102,30 +102,13 @@ leetcode上没有纯01背包的问题,都是01背包应用方面的题目,
|
||||
dp[0][j],即:i为0,存放编号0的物品的时候,各个容量的背包所能存放的最大价值。
|
||||
|
||||
代码如下:
|
||||
|
||||
```
|
||||
// 倒叙遍历
|
||||
for (int j = bagWeight; j >= weight[0]; j--) {
|
||||
dp[0][j] = dp[0][j - weight[0]] + value[0]; // 初始化i为0时候的情况
|
||||
}
|
||||
```
|
||||
|
||||
**大家应该发现,这个初始化为什么是倒叙的遍历的?正序遍历就不行么?**
|
||||
|
||||
正序遍历还真就不行,dp[0][j]表示容量为j的背包存放物品0时候的最大价值,物品0的价值就是15,因为题目中说了**每个物品只有一个!**所以dp[0][j]如果不是初始值的话,就应该都是物品0的价值,也就是15。
|
||||
|
||||
但如果一旦正序遍历了,那么物品0就会被重复加入多次! 例如代码如下:
|
||||
```
|
||||
// 正序遍历
|
||||
for (int j = weight[0]; j <= bagWeight; j++) {
|
||||
dp[0][j] = dp[0][j - weight[0]] + value[0];
|
||||
dp[0][j] = value[0];
|
||||
}
|
||||
```
|
||||
|
||||
例如dp[0][1] 是15,到了dp[0][2] = dp[0][2 - 1] + 15; 也就是dp[0][2] = 30 了,那么就是物品0被重复放入了。
|
||||
|
||||
**所以一定要倒叙遍历,保证物品0只被放入一次!这一点对01背包很重要,后面在讲解滚动数组的时候,还会用到倒叙遍历来保证物品使用一次!**
|
||||
|
||||
|
||||
此时dp数组初始化情况如图所示:
|
||||
|
||||
@ -138,16 +121,23 @@ dp[i][j]在推导的时候一定是取价值最大的数,如果题目给的价
|
||||
|
||||
如果题目给的价值有负数,那么非0下标就要初始化为负无穷了。例如:一个物品的价值是-2,但对应的位置依然初始化为0,那么取最大值的时候,就会取0而不是-2了,所以要初始化为负无穷。
|
||||
|
||||
而背包问题的物品价值都是正整数,所以初始化为0,就可以了。
|
||||
|
||||
**这样才能让dp数组在递归公式的过程中取最大的价值,而不是被初始值覆盖了**。
|
||||
|
||||
如图:
|
||||
|
||||

|
||||
|
||||
最后初始化代码如下:
|
||||
|
||||
```
|
||||
// 初始化 dp
|
||||
vector<vector<int>> dp(weight.size() + 1, vector<int>(bagWeight + 1, 0));
|
||||
for (int j = bagWeight; j >= weight[0]; j--) {
|
||||
dp[0][j] = dp[0][j - weight[0]] + value[0];
|
||||
for (int j = weight[0]; j <= bagWeight; j++) {
|
||||
dp[0][j] = value[0];
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
**费了这么大的功夫,才把如何初始化讲清楚,相信不少同学平时初始化dp数组是凭感觉来的,但有时候感觉是不靠谱的**。
|
||||
@ -239,8 +229,8 @@ void test_2_wei_bag_problem1() {
|
||||
vector<vector<int>> dp(weight.size() + 1, vector<int>(bagWeight + 1, 0));
|
||||
|
||||
// 初始化
|
||||
for (int j = bagWeight; j >= weight[0]; j--) {
|
||||
dp[0][j] = dp[0][j - weight[0]] + value[0];
|
||||
for (int j = weight[0]; j <= bagWeight; j++) {
|
||||
dp[0][j] = value[0];
|
||||
}
|
||||
|
||||
// weight数组的大小 就是物品个数
|
||||
@ -285,8 +275,6 @@ Python:
|
||||
Go:
|
||||
|
||||
|
||||
|
||||
|
||||
-----------------------
|
||||
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
|
||||
* B站视频:[代码随想录](https://space.bilibili.com/525438321)
|
||||
|
@ -104,7 +104,7 @@ for(int i = 0; i < weight.size(); i++) { // 遍历物品
|
||||
|
||||
为什么呢?
|
||||
|
||||
**倒叙遍历是为了保证物品i只被放入一次!**,在[动态规划:关于01背包问题,你该了解这些!](https://mp.weixin.qq.com/s/FwIiPPmR18_AJO5eiidT6w)中讲解二维dp数组初始化dp[0][j]时候已经讲解到过一次。
|
||||
**倒叙遍历是为了保证物品i只被放入一次!**。但如果一旦正序遍历了,那么物品0就会被重复加入多次!
|
||||
|
||||
举一个例子:物品0的重量weight[0] = 1,价值value[0] = 15
|
||||
|
||||
@ -152,7 +152,7 @@ dp[1] = dp[1 - weight[0]] + value[0] = 15
|
||||
|
||||
## 一维dp01背包完整C++测试代码
|
||||
|
||||
```
|
||||
```C++
|
||||
void test_1_wei_bag_problem() {
|
||||
vector<int> weight = {1, 3, 4};
|
||||
vector<int> value = {15, 20, 30};
|
||||
|
Reference in New Issue
Block a user