Leetcode: House Robber

最近做了leetcode上三道动态规划的题,觉得很不错,题目的评价也不错,故作此记录。

1、198. House Robber

题意:一组直线排列的房屋,一个小偷要进屋偷钱,如果小偷偷了相邻两座房屋就会触发报警系统,问在不触发报警系统的前提下小偷最多可以偷到多少钱。

难度:esay

思路:

1、由于是线性排列,所以一间房屋的相邻房屋只有其左右两间房屋,当我们依次查看房屋时,则一间房屋的相邻房屋只剩下其左边的那间房屋;

2、对每间房屋只有两种做法:偷和不偷。如果要偷这间房屋,那上一间房屋不可以偷;如果不偷这间房屋,那上一间房屋可以偷

也可以不偷,看哪种情况下钱会比较多。(由于房屋之间偷与不偷是密切联系的,这里不能认为上一间房屋偷了的话最大钱数肯定比上一间房屋没偷的多)

class Solution {
public:
    int rob(vector<int>& nums) {
        int steal=0,nosteal=0;
        int last_steal=0,last_nosteal=0;
        int n=nums.size();
        
        for(int num:nums)
        {
            last_steal=steal,last_nosteal=nosteal;
            nosteal=max(last_steal,last_nosteal);
            steal=last_nosteal+num;
            
        }
        return max(steal,nosteal);
    }
};

2、213. House Robber II

题意:和上题一样,只是现在所有房屋成环状排列。

难度:medium

思路:

1、和上一题的区别在于,上一题的最后一间房屋在这一题中是与第一间房屋相邻的(环状)。

2、根据上一题的解法和两题的区别,我分为两种情况解题:

①偷第一间房屋:这样的话第二间房屋和最后一间房屋必不可偷,第三间房屋到倒数第二间房屋则可偷可不偷(解法就跟上一题一样了);

②不偷第一间房屋:这样的话从第二间房屋到最后一间房屋是否要偷都不受第一间房屋的影响。(解法跟上题一样);

class Solution {
public:
    int rob(vector<int>& nums) {
        if(nums.empty())
            return 0;
        int ans=0,n=nums.size();
        int last_rob=0,last_no=nums[0];
        int rob=0,no=0;
        //偷了第一家
        for(int i=2;i<n-1;++i)
        {
            rob=last_no+nums[i];
            no=max(last_no,last_rob);
            last_rob=rob,last_no=no;
        }
        ans=max(last_rob,last_no);
        //没偷第一家
        last_no=last_rob=0;
        for(int i=1;i<n;++i)
        {
            rob=last_no+nums[i];
            no=max(last_rob,last_no);
            last_rob=rob,last_no=no;
        }
        return max(ans,max(rob,no));
    }
};

3、337. House Robber III

题意:现在房屋的排列变成了二叉树形式的排列,小偷要从根开始进入该住宅区偷钱。直接相连的两间房屋设为邻居。

思路:

这题我尝试用BFS,按层求解,但失败了,不知道是否可行。然后我看了discuss区域大神的解释,十分详细且简单易懂,推荐直接看大神的解释。我记录大神的最后一种解法。

思路:

前面两题的思路都是“自顶向下”,对于二叉树的形式,采用动态规划的策略来解题的话可以自顶向下也可以自底向上,有时不同方式的选取会导致编程难度的不同,理解问题的角度也不同。(这是我做这题后收获到的,之前我一直习惯性的直接思考自顶向下的做法,从来没转换角度去看问题)

1、每次计算一个节点是否要偷之后我们总可以得到这个节点偷了的话目前的最大钱数的多少以及不偷的话目前的最大钱数是多少。以此,每次对于一个节点的计算,我们可以返回这两种情形得到的最大值,用于计算其父节点在两种情形下的最大钱数。

2、若父节点要偷,则最大钱数为父节点的钱数加上其所有子节点在不偷的时候的最大钱数;若父节点不偷,则最大钱数为其所有子节点在两种情形下的最大钱数之和(即子节点偷与不偷都行,取钱数最大的就好)。

/**
 * 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) {
        if(root==NULL)
            return 0;
        vector<int> ans=robmax(root);
        return max(ans[0],ans[1]);
    }
    //{no_rob,rob}
    vector<int> robmax(TreeNode* root)
    {
        if(!root)
            return {0,0};
        auto left=robmax(root->left);
        auto right=robmax(root->right);
        vector<int> father(2);
        father[0]=max(left[0],left[1])+max(right[0],right[1]);
        father[1]=root->val+left[0]+right[0];
        return father;
    }
};

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值