337.打家劫舍|||
题目描述:
在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。
计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。
思想:
思想1:暴力递归 - 最优子结构
4 个孙子偷的钱 + 爷爷的钱 VS 两个儿子偷的钱 哪个组合钱多,就当做当前节点能偷的最大钱数。这就是动态规划里面的最优子结构
思想2:记忆化 - 解决重复子问题
针对解法一种速度太慢的问题,经过分析其实现,发现爷爷在计算自己能偷多少钱的时候,同时计算了 4 个孙子能偷多少钱,也计算了 2 个儿子能偷多少钱。这样在儿子当爷爷时,就会产生重复计算一遍孙子节点。
针对重复子问题进行优化,我们在做斐波那契数列时,使用的优化方案是记忆化,但是之前的问题都是使用数组解决的,把每次计算的结果都存起来,下次如果再来计算,就从缓存中取,不再计算了,这样就保证每个数字只计算一次。
由于二叉树不适合拿数组当缓存,我们这次使用哈希表来存储结果,TreeNode 当做 key,能偷的钱当做 value
思想3:
换一种办法来定义此问题,每个节点可选择偷或者不偷两种状态,根据题目意思,相连节点不能一起偷
当前节点选择偷时,那么两个孩子节点就不能选择偷了
当前节点选择不偷时,两个孩子节点只需要拿最多的钱出来就行(两个孩子节点偷不偷没关系)
使用一个大小为 2 的数组来表示 vector<int> res(2) 0 代表不偷,1 代表偷
任何一个节点能偷到的最大钱的状态可以定义为
当前节点选择不偷:当前节点能偷到的最大钱数 = 左孩子能偷到的钱 + 右孩子能偷到的钱
当前节点选择偷:当前节点能偷到的最大钱数 = 左孩子选择自己不偷时能得到的钱 + 右孩子选择不偷时能得到的钱 + 当前节点的钱数
代码:
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: int rob(TreeNode* root) { vector<int> res = rob_func(root); return max(res[0],res[1]); } vector<int> rob_func(TreeNode* p){ vector<int> tmp(2); if(p==NULL) return tmp; vector<int> left(2),right(2); if(p->left !=NULL) left = rob_func(p->left); if(p->right !=NULL) right = rob_func(p->right); tmp[0] = max(left[0],left[1]) + max(right[0],right[1]); tmp[1] = left[0] + right[0] + p->val; return tmp; } };
效率较低
改进版:
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: /*用unordered_map保存每个节点的最高金额*/ int rob_dp(TreeNode* root,unordered_map<TreeNode*,int>&info) { if (root==NULL) { return 0; } if (info.find(root)!=info.end()) //如果该节点已经计算过,则不用再重复计算 { return info[root]; } int money=root->val; if (root->left!=NULL) { money+=rob_dp(root->left->left,info)+rob_dp(root->left->right,info); } if (root->right!=NULL) { money+=rob_dp(root->right->left,info)+rob_dp(root->right->right,info); } int result=max(money,rob_dp(root->left,info)+rob_dp(root->right,info)); info.insert(make_pair(root,result)); return result; } int rob(TreeNode* root) { unordered_map<TreeNode*,int>temp; return rob_dp(root,temp); } };