打家劫舍
198.打家劫舍
套用动态规划五部曲,递推公式很好想,一刷ac
1、明确dp数组的含义和下标
dp[ i ]:长度为 i 的数组可偷窃的最高金额为 dp[i]。
2、确定递推公式
dp[i] = max(dp[i-1], dp[i - 2] + nums[i - 1]);
3、初始化dp数组
dp[0] = 0; dp[1] = nums[0];
4、明确遍历顺序
从前向后
5、打印dp数组(debug验证用)
213.打家劫舍II
上一题的变型,分两种情况讨论
情况一:不包含尾部元素
情况二:不包含首部元素
两种情况下分别调用正常的打家劫舍逻辑方法就可以,结果取二者中的较大值。
337.打家劫舍III(树形dp经典入门题、不会)
给出的思路太漂亮了!为什么自己想不到呜呜
以递归三部曲为框架,融合动态规划五部曲
1、明确参数和返回值
我们需要求一个节点 偷与不偷的两个状态所得到的金钱,那么返回值就是一个长度为2的数组。参数为当前节点。
public int[] robTree(Treecode cur)
-
明确dp数组的含义和下标
dp[ i ]:下标为 0 记录不偷该节点所得到的的最大金钱,下标为 1 记录偷该节点所得到的的最大金钱。本题dp数组就是一个长度为2的数组。 dp[0]、dp[1]
2、明确终止条件
-
初始化dp数组
当遍历到空结点时,无论偷还是不偷,都返回{0,0}。同时这也相当于dp数组初始化。
-
明确遍历顺序
首先明确的是使用后序遍历。 因为要通过递归函数的返回值来做下一步计算。
通过递归左节点,得到左节点偷与不偷的金钱。
通过递归右节点,得到右节点偷与不偷的金钱。
3、明确单层递归的逻辑
-
确定递推公式
如果是偷当前节点,那么左右孩子就不能偷,val1 = cur->val + left[0] + right[0]; (如果对下标含义不理解就再回顾一下dp数组的含义)
如果不偷当前节点,那么左右孩子就可以偷,至于到底偷不偷一定是选一个最大的。所以:val2 = max(left[0], left[1]) + max(right[0], right[1]);
最后当前节点的状态就是{val2, val1}; 即:{不偷当前节点得到的最大金钱,偷当前节点得到的最大金钱}
股票问题
121. 买卖股票的最佳时机
这个题思路和讲解不一致,但是一刷也ac了
动规五部曲
1、明确dp数组的含义和下标
dp[ i ]:从第 i 天卖出后,可获得的最大利润为 dp[i]。
2、确定递推公式
dp[i] = max ( 0, dp[i - 1] + nums[i -1] - nums[i - 2] ) ;
如果dp[i - 1] + nums[i -1] - nums[i - 2]为负,说明当前股票不应卖出,继续持有。如果该天卖出可获得的最大利润置为0。
3、初始化dp数组
dp[1] = 0; //买卖不能同一天,所以第1天不能卖出,置为0
4、明确遍历顺序
从前向后
5、打印dp数组(debug验证用)
122.买卖股票的最佳时机II
这里按照讲解的分析
动规五部曲
1、明确dp数组的含义和下标
- dp[i][0] 表示第 i 天持有股票所得现金。
- dp[i][1] 表示第 i 天不持有股票所得最多现金
2、确定递推公式
如果第 i 天持有股票即dp[i][0], 那么可以由两个状态推出来
1、第 i - 1 天就持有股票,那么就保持现状,所得现金就是昨天持有股票的所得现金
即:dp[i - 1][0]
2、第 i 天买入股票,所得现金就是昨天不持有股票的所得现金,减去今天的股票价格
即:dp[i - 1][1] - prices[i]
如果第 i 天不持有股票即dp[i][1]的情况, 依然可以由两个状态推出来
1、第 i - 1 天就不持有股票,那么就保持现状,所得现金就是昨天不持有股票的所得现金
即:dp[i - 1][1]
2、第 i 天卖出股票,所得现金就是按照今天股票价格卖出后,加上昨天持有股票的所得现金
即:prices[i] + dp[i - 1][0]
3、初始化dp数组
dp[0][0] = - prices[0];
dp[0][1] = 0;
4、明确遍历顺序
从前向后
5、打印dp数组(debug验证用)
123.买卖股票的最佳时机III
188.买卖股票的最佳时机IV