题目描述

一个地区的所有房屋排列类似于一棵二叉树,如果直接相连的两个房屋在同一天晚上被打劫房屋将自动报警。
计算在不触发警报的情况下,小偷一晚能够盗取的最高金额。

思路

参考自:https://leetcode-cn.com/problems/house-robber-iii/solution/san-chong-fang-fa-jie-jue-shu-xing-dong-tai-gui-hu/

解法一:暴力递归——最优子结构
使用 爷爷 、两个孩子、四个孙子来说明问题:

  • 首先定义问题的状态:爷爷节点能够获取到的最大钱数:
    1. 明确相邻的节点不能偷,也就是爷爷选择偷,儿子就不能偷了,但是孙子可以偷;
    2. 二叉树只有左右两个孩子,一个爷爷最多两个孩子,四个孙子。
  • 根据以上条件,可以得出单个节点的钱该怎么算:
    • 4个孙子偷的钱 + 爷爷的钱 VS 两个儿子偷的钱 哪个组合钱多,就是当前节点能偷的最大钱数。这就是动态规划中的最优子结构
  • 由于是二叉树,这里可以选择计算所有子节点:
    • 四个孙子偷的钱 + 爷爷的钱如下:
      • int val1 = root.val + rob(root.left.left) + rob(root.left.right) + rob(root.right.left) + rob(root.right.right)
    • 两个孩子偷的钱如下:
      • int val2 = rob(root.left) + rob(root.right)

代码

力扣小白刷题之337题打家劫舍Ⅲ
时间复杂度太高

优化——加一维,消除后效性

当前节点偷或不偷,决定了孩子节点偷或不偷,把这一点设计成状态,放在第二维,这一步称为【消除后效性】。

换种方法来定义此问题:

  • 每个节点可以选择偷和不偷两种状态,根据题意,相连节点不能一起偷。
    • 当前节点选择偷,那么两个孩子节点就不能偷了
    • 当前节点选择不偷,两个孩子节点只需要拿最多的钱出来就行了(两个孩子偷不偷没关系)
  • 使用一个大小为 2 的数组来表示,int[] res = new int[2],0代表不偷,1 代表偷
  • 任何一个节点能偷到的最大的钱的状态可以定义为:
    1. 当前节点选择不偷:当前节点能偷到的最大钱数 = 左孩子能偷到的钱 + 右孩子能偷到的钱
    2. 当前节点选择偷:当前节点能偷到的最大钱数 = 左孩子选择自己不偷时能得到的钱 + 右孩子选择不偷时能得到的钱 + 当前节点的钱数
  • 表示为公式如下:
    • root[0] = Math.max(rob(root.left)[0], rob(root.left)[1]) + Math.max(rob(root.right)[0], rob(root.right)[1]);
    • root[1] = rob(root.left)[0] + rob(root.right)[0] + root.val;

代码

力扣小白刷题之337题打家劫舍Ⅲ

相关文章: