[leetcode] HouseRobI, II, III

本文探讨了三类房屋抢劫问题:线性排列、闭环排列和树状排列的房屋抢劫算法,采用动态规划策略,详细解析了算法原理及其实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

HouseRob I

  • 问题描述:一个强盗要抢劫一个房屋,但是他不能抢连续的两个房间,比如说ABC房间,他只能抢A和C或者是B,否则就会触发警报,将其抓住。我们知道每个房间都有money,并且money>=0的。
  • 这个题我们可以看作动态规划来解决。
    • 划分子问题 dp[i]表示的是从第1个房间到第i个房间,所能抢到的最大金额。
    • 根据子问题求解父问题 dp[i] = max(dp[i-2] + nums[i], dp[i-1])。从第0个到第i个的最大值等于(1)第0个到第i-1个最大值或者(2)0~i-2+nums[i] (因为money一定是正的所以可以直接加)。 最大值就等于这两个里面较大的一个。
  • 代码:
int rob(vector<int>& nums) {
        int size = (int) nums.size();
        if(size == 0)
            return 0;
        int dp[size];
        memset(dp, 0, sizeof(dp));
        dp[0] = nums[0];
        int res = dp[0];
        for(int i=1;i<size;i++){
            if((i-2)>=0){
                dp[i] = max(dp[i-2] + nums[i], dp[i-1]);
            }else{
                dp[i] = max(nums[i], dp[i-1]);
            }
            res = max(res, dp[i]);
        }
        return res;
    }

HouseRob II

  • 问题描述:基本和HouseRobI一致,不同之处在于HouseRob II里面收尾也是相连的。也就是说不能同时取第一个和最后一个房间。
  • 分析,假设我们的数组是从[0~N]. 因为我们不能同时取第一个和最后一个。所以我们可以计算[0~N-1]的最大值Res1和[1 ~ N]的最大值Res2。这样我们就不可能同时取到第一个和最后一个。
  • 现在有一个问题,即如何确保上述的两个最大值里面较大的一个就是我们最终的结果。
    • 假设结果不包含在上述两个里面,因为结果不同时包括第一个和最后一个。
    • 如果说最终的结果包含第一个元素不包含最后一个元素。则这样的解一定是Res1。
    • 如果最终的结果包含最后一个不包括第一个元素,则这样的解一定是Res2。
    • 如果最终的结果既不包含第一个也不包含第二个,则这样的解一定是Res1=Res2。
    • 综上,可以得到Res1和Res2里面的较大值一定是我们最后的结果。
  • 代码:
int rob(const vector<int>& nums, int l, int r){
        int size = r - l + 1;
        int dp[size];
        memset(dp, 0, sizeof(dp));
        dp[0] = nums[l];
        int res = dp[0];
        for(int i=l+1; i<=r; i++){
            if((i-2) >= l){
                dp[i-l] = max(dp[i-l-2] + nums[i], dp[i-l-1]);
            }else{
                dp[i-l] = max(nums[i], dp[i-l-1]);
            }
            res = max(res, dp[i-l]);
        }
        return res;
    }
int rob(vector<int>& nums) {
        int size = (int) nums.size();
        return max(rob(nums, 0, size-2), rob(nums, 1, size-1));
}

HouseRob III

  • 问题描述:和HouseRob一样,不同之处在于本题中房屋不是线性排列,而是呈现一个二叉树状的排列。同样抢了某个房子就不能抢与其相邻的房子(父节点和孩子节点)。
  • 分析:
    • 划分成子问题,假设说我们的根节点是R,我们将其拆分计算子节点的最大值。
    • 子问题的解构建父问题的解,假设说我们知道了子节点的最大值,那么我们计算父节点的最大值呢?
      • 因为不能同时取相邻的两个节点
      • 所以最大值={父节点的值+左子树中不包括左孩子的最大值+右子树中不包括右孩子的最大值,左子树的最大值+右子树的最大值}中的最大值
      • 有人可能会有疑问,那如果左子树中的最大值本来就不包括左孩子呢?那这种情况本身就应该在第一种解中。
    • 上面我们说明了,我们不仅要算每个节点的最大值,还需要计算不包括当前节点的最大值。
  • 伪代码:
    1. 输入:根节点R
    2. 输出:最大值Res1和不包括R的最大值Res2.注意Res1可能等于Res2
    3. 如果R等于NULL,则返回0, 0
    4. 计算左子树的Res1leftRes1_{left}Res1leftRes2leftRes2_{left}Res2left
    5. 计算右子树的Res1rightRes1_{right}Res1rightRes2rightRes2_{right}Res2right
    6. 下面开始计算本节点的Res1Res1Res1Res2Res2Res2
      1. Res1=max(R.val+Res2left+Res2right,Res1left+Res1right)Res1 = max(R.val + Res2_{left} + Res2_{right}, Res1_{left} + Res1_{right})Res1=max(R.val+Res2left+Res2right,Res1left+Res1right)
      2. Res2=Res1left+Res1rightRes2 = Res1_{left} + Res1_{right}Res2=Res1left+Res1right
    7. 返回Res1和Res2
  • 代码:
class Result{
public:
    int low_level;
    int high_level;
    Result(){
        low_level = 0;
        high_level = 0;
    }
};
Result robBase(TreeNode* root){
        if(root == NULL)
        {
            return Result();
        }
        Result left = robBase(root->left);
        Result right = robBase(root->right);
        int max_value = max(root->val + left.low_level + right.low_level, left.high_level + right.high_level);
        Result res;
        res.high_level = max_value;
        res.low_level = left.high_level + right.high_level;
        return res;

    }
    int rob(TreeNode* root) {
         // int res = robBase(root, true);
        if(root == NULL)
            return 0;
        Result res = robBase(root);
        return res.high_level;
    }
class Solution(object):
    def robBase(self, root):
        if root is None:
            return 0, 0
        l1, l2 = self.robBase(root.left)
        r1, r2 = self.robBase(root.right)
        res = max([l1 + r1, root.val + l2 + r2])
        return res, l1 + r1
    def rob(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        l1, l2 = self.robBase(root)
        return max([l1, l2])

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值