学习动态规划-打家劫舍

有一个聪明的小偷计划偷窃藏在房屋中的现金,仅当相邻的房屋同一晚上失窃时才触发报警装置,问在不触发报警情况下,小偷一个晚上能够偷窃的最大现金数量。

房屋的排列方式各式各样,分别如下:

1,线性排列

来源:198. 打家劫舍

解题思路:动态规划

拆解子问题,dp[i]定义为偷窃前i个房屋的最大金额,那么

  • 如果偷窃nums[i-1],那么nums[i]不能偷窃,则dp[i]=dp[i-1];
  • 如果没有偷窃nums[i-1],那么nums[i]可偷窃,则dp[i]=nums[i] + dp[i-2];

综合二者取最大,得dp[i] = max{dp[i-1], dp[i-2]+nums[i]}

class Solution {
public:
    int rob(vector<int>& nums) {
        // 为了处理前2房屋,定义dp比nums长度多1,dp[i+1]对应nums[i]
        vector<int> dp(nums.size() + 1);
        dp[0] = 0;
        dp[1] = nums[0];
        for (int i = 1; i < nums.size(); i++) {
            dp[i+1] = max(dp[i], dp[i-1] + nums[i]);
        }
        return dp[nums.size()];
    }
};

空间优化:

class Solution {
public:
    int rob(vector<int>& nums) {
        int one = 0, two = nums[0];
        for (int i = 1; i < nums.size(); i++) {
            int three = max(two, one + nums[i]);
            one = two;
            two = three;
        }
        return two;
    }
};

2,围成一圈

来源:213. 打家劫舍 II

解题思路:

去掉其中一个房屋,将其变成线性的,如下图,

去掉0号房屋则为:

去掉11号房屋则为:

其他房屋也可以去掉,但是从代码角度来看,房屋的存储用的是一位数组,首位2个元素去掉后其余元素依然连续,方便处理。

class Solution {
public:
    int robRange(const vector<int>& nums, int start, int end) {
        int one = 0, two = nums[start];
        for (int i = start+1; i < end; i++) {
            int three = max(two, one + nums[i]);
            one = two;
            two = three;
        }
        return two;
    }

    int rob(vector<int>& nums) {
        int length = nums.size();
        if (length == 1) {
            return nums[0];
        } else if (length == 2) {
            return max(nums[0], nums[1]);
        }
        return max(robRange(nums, 0, length - 1), robRange(nums, 1, length));
    }
};

3,二叉树型

来源:337. 打家劫舍 III

解题思路:自顶向下递归

分成两种情况讨论:

  1. 抢劫根节点:最大金额 = 根节点 + 左子树中不抢根节点的最大金额 + 右子树中不抢根节点的最大金额
  2. 不抢根节点:最大金额 = 左子树抢与不抢根节点最大金额 + 右子树抢与不抢根节点最大金额
class Solution {
public:
    int rob(TreeNode* root) {
        int max1, max2;
        rob(root, &max1, &max2);
        return max(max1, max2);
    }
    // max1:抢劫根节点,max2:不抢根节点
    void rob(TreeNode* root, int *max1, int *max2) {
        if (root == NULL) {
            *max1 = 0;
            *max2 = 0;
            return;
        }
        int left_max1, left_max2;
        rob(root->left, &left_max1, &left_max2);
        int right_max1, right_max2;
        rob(root->right, &right_max1, &right_max2);

        *max1 = root->val + left_max2 + right_max2; // 抢劫根节点
        *max2 = max(left_max1, left_max2) + max(right_max1, right_max2); // 不抢根节点
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
打家劫舍是一个经典的动态规划问题。根据引用\[2\]中的解法,我们可以使用动态规划来解决这个问题。假设偷窃房屋的下标范围是\[start, end\],用dp\[i\]表示在下标范围\[start, i\]内可以偷窃到的最高总金额。根据状态转移方程dp\[i\] = max(dp\[i − 2\] + nums\[i\], dp\[i − 1\]),我们可以通过递推得到最终的结果。 在解决这个问题时,我们可以使用滚动数组来降低空间复杂度,只需要存储前两间房屋的最高总金额,将空间复杂度降到O(1)。具体的代码实现可以参考引用\[2\]中的代码。 另外,对于打家劫舍 III这个问题,根据引用\[3\]中的解法,我们可以使用递归的方式来解决。对于每个节点,我们可以选择偷取该节点的钱和其四个孙子节点的钱,或者选择偷取其两个儿子节点的钱,然后比较两种方案的钱数,选择钱数较多的方案作为当前节点能偷取的最大钱数。 具体的代码实现可以参考引用\[3\]中的代码。 综上所述,我们可以使用动态规划来解决打家劫舍问题,具体的解法可以参考引用\[2\]中的代码。对于打家劫舍 III这个问题,可以使用递归的方式来解决,具体的解法可以参考引用\[3\]中的代码。 #### 引用[.reference_title] - *1* *2* *3* [动态规划解决打家劫舍问题](https://blog.csdn.net/p1967914901/article/details/125372599)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值