337. 打家劫舍3(Python)

题目

难度:★★★★☆
类型:二叉树
方法:动态规划,深度优先搜索

传送门

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

计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。

示例

示例 1:

输入: [3,2,3,null,3,null,1]

     3
    / \
   2   3
    \   \
     3   1

输出: 7
解释: 小偷一晚能够盗取的最高金额 = 3 + 3 + 1 = 7.

示例 2:

输入: [3,4,5,1,3,null,1]

     3
    / \
   4   5
  / \   \
 1   3   1

输出: 9
解释: 小偷一晚能够盗取的最高金额 = 4 + 5 = 9.

解答

这里我们需要使用动态规划来做,跟数组方式排列的类似,但是这里是以树的形式排列的,因此我们需要使用深度优先搜索的方法,来模型数组形式的遍历。

题目重新描述:获取以root为根节点的树,加数在树的结构上不相邻的前提下,求最大权值和。我们用“求root的rob值”来简称这个问题。

定义的深度优先搜索函数有一个输入和两个输出,输入是一个树的根节点,因为获取到了根节点,就相当于得到了整个社区的组织结构,我们将它命名为root社区。输出有两个,第一个元素是“root”的rob值,也就是root社区的累计最大收益,此时对于root所在家庭来说,可能被盗也可能不会,取决于怎样使得小偷的收益最大,,第二个元素是不偷取root所在家庭,root左孩子和右孩子社区的累计效益之和。我们将这两个变量分别命名为rob_cur_max和not_rob_cur_max。

初始状态:当root为None时,返回两个零;
其他情况下,递归获取左孩子和右孩子的rob_cur_max和not_rob_cur_max两个参数,得到left_pre, left_pre_pre,right_pre, right_pre_pre;
递推公式:rob_cur_max 有两种情况,一种是偷root所在家庭,root.val + left_pre_pre + right_pre_pre,另一种是不偷,left_pre + right_pre,取最大值就好。

class Solution(object):
    def rob(self, root):

        """
        我们将函数的功能描述为:获取root社区的rob值。
        :type root:
        :rtype: int
        """
        def dfs(root):
            """
            获取以root为根节点的社区的最大盗窃金额
            :param root: 根节点
            :return: 返回两个数值,一个是root和rob值,另一个是root左右子树rob值之和。
            """
            if not root:
                return 0, 0
            left_pre, left_pre_pre = dfs(root.left)
            right_pre, right_pre_pre = dfs(root.right)
            return max(root.val + left_pre_pre + right_pre_pre, left_pre + right_pre), left_pre + right_pre

        return dfs(root)[0]

如有疑问或建议,欢迎评论区留言~

你可能感兴趣的:(337. 打家劫舍3(Python))