Day50 代码随想录算法训练营 第九章 动态规划part09|198.打家劫舍、213.打家劫舍Ⅱ、337.打家劫舍Ⅲ

今日内容

198.打家劫舍

213.打家劫舍Ⅱ

337.打家劫舍Ⅲ

198.打家劫舍

问题:

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

 提示:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 400

思路

动态规划五部曲:

① 确定dp数组以及下标的含义

dp[i]:考虑下标i(包括i以内)的放物,最多可以偷窃的金额为dp[i]

② 递推公式(偷 vs 不偷 的问题)

考虑到相邻的房屋不能同时偷:

偷第i房,则第i-1房不能考虑,找出 【0,i-2】以内的房屋中,最多的偷窃金额 + nums【i】

不偷第i房,找出 【0,i-1】以内的房屋中,最多的偷窃金额 dp[i]

偷和不偷,取决于,总金额谁大

dp[i] = max(dp[i - 2] + nums[i], dp[i - 1])

 

可以看出,递推公式的基础就是dp[0] 和 dp[1]

③ dp数组如何初始化

dp[0] 一定是 nums[0]

dp[1]就是nums[0]和nums[1]的最大值即:dp[1] = max(nums[0], nums[1])

注意定义:[0,i] 房屋内 的最多偷盗金额

④ 确定遍历顺序

从前到后

 1 for (int i = 2; i < nums.size(); i++) { 2 dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]); 3 } 

⑤ 打印

代码:

 1 /*
 2  * @lc app=leetcode.cn id=198 lang=javascript
 3  *
 4  * [198] 打家劫舍
 5  */
 6 
 7 // @lc code=start
 8 /**
 9  * @param {number[]} nums
10  * @return {number}
11  */
12 var rob = function(nums) {
13     let dp = new Array(nums.length).fill(0)
14     dp[0] = nums[0];
15     dp[1] = Math.max(nums[0], nums[1]);
16     for (let i = 2; i < nums.length; i++) {
17         dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i])
18     }
19     return dp[nums.length - 1]
20 };

213.打家劫舍Ⅱ

问题:

你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。

给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。

 

 提示:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 1000

思路:

环形 拆成 线形

 

 注意:考虑范围,而不是必选!

 

 1 /*
 2  * @lc app=leetcode.cn id=213 lang=javascript
 3  *
 4  * [213] 打家劫舍 II
 5  */
 6 
 7 // @lc code=start
 8 /**
 9  * @param {number[]} nums
10  * @return {number}
11  */
12 var rob = function(nums) {
13 
14     if (nums.length === 1) return nums[0];
15     else if (nums.length === 2) return Math.max(nums[0], nums[1]);
16     else if (nums.length === 3) return Math.max(nums[0], Math.max(nums[1], nums[2]))
17     let res1 = robRange(nums, 1, nums.length - 1) //[0,nums.length-2]
18     let res2 = robRange(nums, 0, nums.length - 2) //[1,nums.length-1]
19 
20     return Math.max(res1, res2);
21 
22     function robRange(nums, start, end) {
23         let dp = new Array(nums.length).fill(0)
24         dp[start] = nums[start];
25         dp[start + 1] = Math.max(nums[start], nums[start + 1])
26 
27         for (let i = start + 2; i <= end; i++) {
28             dp[i] = Math.max(dp[i - 2] + nums[i], dp[i - 1])
29         }
30         return dp[end];
31     }
32 };

337.打家劫舍Ⅲ 【不会】

 问题:

小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root 。

除了 root 之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果 两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。

给定二叉树的 root 。返回 在不触动警报的情况下 ,小偷能够盗取的最高金额 。

 

 提示:

  • 树的节点数在 [1, 104] 范围内
  • 0 <= Node.val <= 104

思路:

使用一个长度为2的数组,记录当前节点偷与不偷所得到的的最大金钱

① dp数组:

每个节点只有两个状态:偷还是不偷

dp[0]:不偷,最大金钱

dp[1]:偷,最大金钱

dp[0] 和 dp[1] 分别表示偷和不偷的两个状态

② 递归三部曲

1.确定函数与参数:

 

2.终止条件:

遇到空节点

if (cur == NULL) return vector<int>{0, 0};

3.确定遍历顺序:

后序遍历

// 下标0:不偷,下标1:偷
vector<int> left = robTree(cur->left); // 左
vector<int> right = robTree(cur->right); // 右
// 中
 4.确定单层递归的逻辑:
偷当前节点,左右孩子均不偷
val1 = cur->val + left[0] + right[0]

不偷当前节点,偷左右孩子(选一个最大的)

val2 = max(left[0], left[1]) + max(right[0], right[1]);

当前节点的状态就是{val2, val1}

vector<int> left = robTree(cur->left); // 左
vector<int> right = robTree(cur->right); // 右

// 偷cur
int val1 = cur->val + left[0] + right[0];
// 不偷cur
int val2 = max(left[0], left[1]) + max(right[0], right[1]);
return {val2, val1};


没弄懂
 1 /*
 2  * @lc app=leetcode.cn id=337 lang=javascript
 3  *
 4  * [337] 打家劫舍 III
 5  */
 6 
 7 // @lc code=start
 8 /**
 9  * Definition for a binary tree node.
10  * function TreeNode(val, left, right) {
11  *     this.val = (val===undefined ? 0 : val)
12  *     this.left = (left===undefined ? null : left)
13  *     this.right = (right===undefined ? null : right)
14  * }
15  */
16 /**
17  * @param {TreeNode} root
18  * @return {number}
19  */
20 var rob = function(root) {
21 
22     // 后序遍历函数
23     const postOrder = node => {
24         // 递归出口
25         if (!node) return [0, 0];
26         // 遍历左子树
27         const left = postOrder(node.left);
28         // 遍历右子树
29         const right = postOrder(node.right);
30         // 不偷当前节点,左右子节点都可以偷或不偷,取最大值
31         const DoNot = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
32         // 偷当前节点,左右子节点只能不偷
33         const Do = node.val + left[0] + right[0];
34         // [不偷,偷]
35         return [DoNot, Do];
36     };
37     const res = postOrder(root);
38     // 返回最大值
39     return Math.max(...res);
40 
41 };

 

posted @ 2023-10-13 15:32  子酱  阅读(71)  评论(0)    收藏  举报