前言
动态规划专题,从简到难通关动态规划。
每日一题
今天的题目是 337. 打家劫舍 III
小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root 。
除了 root 之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果 两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。
给定二叉树的 root 。返回 在不触动警报的情况下 ,小偷能够盗取的最高金额 。
示例 1:
输入: root = [3,2,3,null,3,null,1]
输出: 7
解释: 小偷一晚能够盗取的最高金额 3 + 3 + 1 = 7
示例 2:
输入: root = [3,4,5,1,3,null,1]
输出: 9
解释: 小偷一晚能够盗取的最高金额 4 + 5 = 9
提示:
- 树的节点数在
[1, 104]范围内 0 <= Node.val <= 104
题解
递归
这道题给出了一棵二叉树,每个节点代表一个房屋,在相邻的房屋中只能选择偷取其中的一个,问可以获得的最大价值是多少。
针对这个问题,我们可以使用递归求解。具体来说,对于当前节点,定义一个长度为2的数组res,res[0]表示不偷当前节点获得的最大价值,res[1]表示偷当前节点获得的最大价值,然后递归计算当前节点左右子树不偷和偷的最大价值,最后根据当前节点加上子树的值,得到当前节点偷或不偷所获得的最大价值,然后更新res数组。
递归出口是对于空节点返回[0, 0]。最后返回解为根节点res数组中的两个值中的较大值。
在求解当前节点的最大价值时,只有两个选项:偷当前节点或者不偷当前节点,因为如果偷了当前节点,那么左右子树的子节点都不能偷了;如果不偷当前节点,那么左右子树的节点可以根据需要选取,取到数量的最大值。因此,利用这种深度优先遍历的方式一定能够得到正确的最大价值。
/**
* Definition for a binary tree node.
* class TreeNode {
* val: number
* left: TreeNode | null
* right: TreeNode | null
* constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
* }
*/
function rob(root: TreeNode | null): number {
if (!root) {
return 0;
}
if (!root.left && !root.right) {
return root.val;
}
let val1 = root.val;
if (root.left) {
val1 += rob(root.left.left) + rob(root.left.right);
}
if (root.right) {
val1 += rob(root.right.left) + rob(root.right.right);
}
const val2 = rob(root.left) + rob(root.right);
return Math.max(val1, val2);
};
动态规划
- 确定dp数组以及下标的含义
dp[i][0] 表示在 i 节点不选择时所能获得的最大价值;dp[i][1] 表示在 i 节点选择时所能获得的最大价值。
- 确定递推公式
因为一个节点只有两种状态,即偷和不偷,所以递推公式很简单易得:
dp[i][0] = max(dp[left][0], dp[left][1]) + max(dp[right][0], dp[right][1]);
dp[i][1] = dp[left][0] + dp[right][0] + node_val;
对于任何一个节点,都有两种情况: 偷:那么不能选择当前节点的左右子节点。 不偷:那么可以选择左右子节点的最大值。
- dp数组如何初始化
对于 NULl 或叶子节点,其 dp 值均为 [0,0]。
- 确定遍历顺序
从叶子节点往根节点的顺序进行计算。
代码:
/**
* Definition for a binary tree node.
* class TreeNode {
* val: number
* left: TreeNode | null
* right: TreeNode | null
* constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
* }
*/
function rob(root: TreeNode | null): number {
const dfs = (node) => {
if (!node) return [0, 0];
const left = dfs(node.left);
const right = dfs(node.right);
const dp = new Array(2).fill(0);
dp[1] = node.val + left[0] + right[0];
dp[0] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
return dp;
}
const res = dfs(root);
return Math.max(res[0], res[1]);
};