337.打家劫舍|||

题目描述:

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

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

 

思想:

思想1:暴力递归 - 最优子结构

  4 个孙子偷的钱 + 爷爷的钱 VS 两个儿子偷的钱 哪个组合钱多,就当做当前节点能偷的最大钱数。这就是动态规划里面的最优子结构

思想2:记忆化 - 解决重复子问题

  针对解法一种速度太慢的问题,经过分析其实现,发现爷爷在计算自己能偷多少钱的时候,同时计算了 4 个孙子能偷多少钱,也计算了 2 个儿子能偷多少钱。这样在儿子当爷爷时,就会产生重复计算一遍孙子节点。

  针对重复子问题进行优化,我们在做斐波那契数列时,使用的优化方案是记忆化,但是之前的问题都是使用数组解决的,把每次计算的结果都存起来,下次如果再来计算,就从缓存中取,不再计算了,这样就保证每个数字只计算一次。
由于二叉树不适合拿数组当缓存,我们这次使用哈希表来存储结果,TreeNode 当做 key,能偷的钱当做 value

思想3:

换一种办法来定义此问题,每个节点可选择偷或者不偷两种状态,根据题目意思,相连节点不能一起偷

当前节点选择偷时,那么两个孩子节点就不能选择偷了
当前节点选择不偷时,两个孩子节点只需要拿最多的钱出来就行(两个孩子节点偷不偷没关系)
使用一个大小为 2 的数组来表示 vector<int> res(2)  0 代表不偷,1 代表偷
任何一个节点能偷到的最大钱的状态可以定义为

当前节点选择不偷:当前节点能偷到的最大钱数 = 左孩子能偷到的钱 + 右孩子能偷到的钱
当前节点选择偷:当前节点能偷到的最大钱数 = 左孩子选择自己不偷时能得到的钱 + 右孩子选择不偷时能得到的钱 + 当前节点的钱数

 

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

 

代码:

/**
 * 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);
    }
};

 

posted @ 2020-05-06 17:57  thefatcat  阅读(169)  评论(0编辑  收藏  举报