leetcode-打家劫舍 III

100 阅读2分钟

「这是我参与2022首次更文挑战的第7天,活动详情查看:2022首次更文挑战」。

上班路上路过社区医院,好多人在排队做核酸检测,这已经是各地回家过年前必备步骤了,希望这样的场景尽早消散吧。

题目

在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。
计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。

示例 1:
输入: [3,2,3,null,3,null,1]
image.png 输出: 7
解释: 小偷一晚能够盗取的最高金额 = 3 + 3 + 1 = 7.

示例 2:
输入: [3,4,5,1,3,null,1]
image.png 输出: 9
解释: 小偷一晚能够盗取的最高金额 = 4 + 5 = 9.

思路

经典题目打家劫舍的变种。
不过这种题目有一个共同点,就是状态需要分开来考虑,才能比较容易得到状态转义方程。比如这题,直接定义状态dp[node]代表这个节点作为根节点可以盗取的最好金额的话,发现没法转移了。 我们把dp[node]分成2个状态:select代表选择当前节点,可以获取的最大金额;skip代表不选择当前节点,可以获取的最大金额,那么首先,刚刚的

dp = max(select, node). 接下来我们看状态转移方程,如果选择了当前节点,那么他的2个子节点肯定就不能选择,所以 select[node] = skip[node.left] + skip[node.right] 如果当前节点没有选择,那么它的左右子节点可以选择也可以不选择,反正取到左右子树能获取的最大金额就好 skip[node] = dp[node.left] + dp[node.right] 整理一下,就是: skip[node] = max(select[node.left],skip[node.left]) + max(select[node.right],skip[node.right])

Java版本代码

class Solution {
    public int rob(TreeNode root) {
        Map<TreeNode, Integer> selectMap = new HashMap<>();
        Map<TreeNode, Integer> skipMap = new HashMap<>();
        dfs337(root, selectMap, skipMap);
        return Integer.max(selectMap.get(root), skipMap.get(root));
    }

    private static void dfs337(TreeNode node, Map<TreeNode, Integer> selectMap, Map<TreeNode, Integer> skipMap) {
        if (node == null) {
            return;
        }
        dfs337(node.left, selectMap, skipMap);
        dfs337(node.right, selectMap, skipMap);
        selectMap.put(node, node.val + skipMap.getOrDefault(node.left, 0) + skipMap.getOrDefault(node.right, 0));
        skipMap.put(node,
                Integer.max(selectMap.getOrDefault(node.left, 0), skipMap.getOrDefault(node.left, 0))
                        + Integer.max(selectMap.getOrDefault(node.right, 0), skipMap.getOrDefault(node.right, 0)));
    }
}