题目描述
小偷找到了一个新的偷盗地点,这里地区的房子组成了一棵二叉树,地区的入口是二叉树的根所在的房子。如果小偷同时偷窃了两个直接相邻的房子,就会触发警报器。求在不触发警报器的情况下小偷可以抢到的最多的money。
Example:
3
/ \
2 3
\ \
3 1
小偷可以抢到的最多的money是3+3+1=7(偷窃带下划线的房子)
算法分析
本题是House Robber的follow up。House Robber-i中房子排列成一个序列,House Robber-ii中房子排列成一个环,均可以用动态规划解决。
这里简单分析一下房子排成一个序列的情况,直觉上我们应该尽量偷价值高的房子,但因为有限制条件不能偷相邻的房子,直接贪心不可行。考虑动态规划,如果我们已知前k个房子可能偷到的最高价值,当k等于n时我们也就得到了全局最优解;同时,前k个房子能偷到的最高价值可以通过对第k个房子做决策(偷或者不偷),从比k小的局部最优解中得到。
对于本问题,由于是树上的问题, 我们有一种dp类型是,在树上一边搜索,一边利用dp数组保存状态,所以我们叫他树形dp,这是一类型大家没怎么见过的新题型。假设我们已知了根节点的左右子树的局部最优解,通过对根节点所在的房子进行决策:偷或者不偷,我们就可以得到全局最优解。具体算法过程如下: 递归先求得左右子树的局部最优解,分别表示该子树根节点的房子偷和不偷所能得到的最高价值,再得到了左右子树的返回值后,对于本层更新,我们需要考虑,1.左右儿子不偷的时候我们当前可以偷本层,2.左右儿子都偷的时候我们当前本层则不能偷。
具体动态规划四要素为:
Definition:
dp[i][0]表示以i为根的子树不偷根节点能获得的最高价值,dp[i][1]表示以i为根的子树偷根节点能获得的最高价值
Function:
dp[i][0] = max(dp[left[i]][0], dp[left[i]][1]) + max(dp[right[i]][0], dp[right[i]][1]);
dp[i][1] = dp[left[i]][0] + dp[right[i]][0];
Intialize:
空节点的dp值均为0
Answer:
dp[root][0]和dp[root][1]中的较大值
最后返回根节点的两个dp值中较大的那个即可。
/** * Definition of TreeNode: * class TreeNode { * public: * int val; * TreeNode *left, *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * } */ class Solution { private: void visit(TreeNode* root, int &rob, int ¬_rob) { rob = not_rob = 0; if (root == NULL) return; int left_rob, left_not_rob, right_rob, right_not_rob; visit(root->left, left_rob, left_not_rob); visit(root->right, right_rob, right_not_rob); rob = left_not_rob + right_not_rob + root->val; not_rob = max(left_rob, left_not_rob) + max(right_rob, right_not_rob); } public: /** * @param root: The root of binary tree. * @return: The maximum amount of money you can rob tonight */ int houseRobber3(TreeNode* root) { // write your code here int rob, not_rob; visit(root, rob, not_rob); return max(rob, not_rob); } };
面试官角度分析
本题可以用搜索解决,但面试官考察的是对树和动态规划的理解。给出基于动态规划思想的O(n)可达到hire的程度,n为总房子数