一、

    你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

    给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

    示例 1:
    输入:[1,2,3,1]
    输出:4
    解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
    偷窃到的最高金额 = 1 + 3 = 4 。

    示例 2:
    输入:[2,7,9,3,1]
    输出:12
    解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
    偷窃到的最高金额 = 2 + 9 + 1 = 12 。

    提示:
    0 <= nums.length <= 100
    0 <= nums[i] <= 400

    1. int rob(vector<int>& nums) {
    2. int n = nums.size();
    3. if (n == 0)
    4. return 0;
    5. else if(n == 1)
    6. return nums[0];
    7. else if (n == 2){
    8. return nums[0] > nums[1] ? nums[0] : nums[1];
    9. }
    10. // 动态规划:dp[i]表示抢到第i个房子为止的最大金额;
    11. // 分析:
    12. // 如果抢第i个房子,则积累的总金额为dp[i-2] + nums[i];
    13. // 如果不抢第i个房子,则积累的总金额为dp[i-1], 也就是等于抢到第i-1个房子时候的最大金额。
    14. // 状态转移:dp[i] = max(dp[i-2]+nums[i], dp[i-1]);
    15. // 状态初始化: dp[0] = nums[0], dp[1] = max(nums[0], nums[1]);
    16. int * dp = new int[n];
    17. dp[0] = nums[0];
    18. dp[1] = max(nums[0], nums[1]);
    19. for(int i=2; i<n; ++i){
    20. dp[i] = max(dp[i-2]+nums[i], dp[i-1]);
    21. }
    22. int max_money = dp[n-1];
    23. delete [] dp;
    24. return max_money;
    25. }

    二、

    你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都围成一圈,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

    给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。

    示例 1:
    输入: [2,3,2]
    输出: 3
    解释: 你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。

    示例 2:
    输入: [1,2,3,1]
    输出: 4
    解释: 你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
    偷窃到的最高金额 = 1 + 3 = 4 。

    class Solution {
    public:
        int rob(vector<int>& nums) {
            int n = nums.size();
            if (n == 0)
                return 0;
            else if(n == 1)
                return nums[0];
            else if(n == 2)
                return max(nums[0], nums[1]);
    
            // 动态规划: dp[i]表示小偷偷到第i个房间时获取的最大金额数
            // 由于本题中规定,房子是环绕一圈的,所以如果从第一个房子开始抢,
            //                                则不能抢第n个房子;从[1~n-1]
            // 如果要抢第n个房子,则必须从第二个房子开始抢。从[2~n]
            // 所以,分两种情况,然后取最大抢劫金额。
            int *dp0 = new int[n];
    
            dp0[0] = nums[0];
            dp0[1] = max(nums[0], nums[1]);
            for(int i=2; i<n-1; ++i){
                dp0[i] = max(dp0[i-2]+nums[i], dp0[i-1]);
            }
    
            int *dp1 = new int[n];
            dp1[1] = nums[1];
            dp1[2] = max(nums[1], nums[2]);
            for(int i=3; i<n; ++i){
                dp1[i] = max(dp1[i-2]+nums[i], dp1[i-1]);
            }
    
            int max_money = max(dp0[n-2], dp1[n-1]);
            delete [] dp0;
            delete [] dp1;
            return max_money;
    
            // 上述代码可以优化空间复杂度为: O(1)
        }
    };
    

    三、

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

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

    示例 1:

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

     3<br />    / \<br />   2   3<br />    \   \ <br />     3   1
    

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

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

     3<br />    / \<br />   4   5<br />  / \   \ <br /> 1   3   1
    

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

    /**
     * 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:
        // 递归
        // 分两种情况讨论:
        // 1、打劫根节点,则接下来只能打劫根节点左右子节点的子节点。
        // 2、不打劫根节点,则打劫的收益为根节点的左右子树收益和。
        /*
        int rob(TreeNode* root) {
            if (!root)
                return 0;
            int max_money1 = root->val;
            if (root->left)
                max_money1 += rob(root->left->left) + rob(root->left->right);
            if (root->right)
                max_money1 += rob(root->right->left) + rob(root->right->right);
    
            int max_money2 = rob(root->left) + rob(root->right);
            return max(max_money1, max_money2);
        }
        */
        // 超过时间限制:里面有很多重复的计算;
    
        // 动态规划+hash_map+二叉树后续遍历
        unordered_map<TreeNode*, int> f, g;
        // 采用hash_map减少重复计算量
        // 采用f, g记录某一个房间节点node被盗取和没被盗取时候盗取的最高累计金额val;
        // 一、当节点房间node被盗取的时候,小偷不能访问其子节点房间,只能访问其子节点的子节点房间,
        // 所以f(node)等于其左(l)右(r)子节点不被选中的盗取金额之和:f(node) = o->val + g(l) + g(r);
    
        // 二、当节点node不被盗取的时候,小偷可以访问其左之结点和右子节点,也可以不访问:依据是否选中贡献值大做选择
        // 所以g(node) = max(f(l), g(l)) + max(f(r), g(r));
        void dfs(TreeNode* node){
            if (!node)
                return;
            // 左->右->根
            dfs(node->left);
            dfs(node->right);
            f[node] = node->val + g[node->left] + g[node->right];
            g[node] = max(f[node->left], g[node->left]) + max(f[node->right], g[node->right]);
        }
        int rob(TreeNode* root) {
            dfs(root);
            return max(f[root], g[root]);
        }
    };
    

    欢迎交流,批评指正!