自学内容网 自学内容网

代码随想录算法训练营(动态规划9)|198.打家劫舍 & 213.打家劫舍II & 337.打家劫舍III

今天就是打家劫舍的一天,微笑

198.打家劫舍

leetcode题目链接
视频讲解
文章讲解

动规五部曲分析如下:

  1. 确定dp数组(dp table)以及下标的含义
    dp[i]:考虑下标i(包括i)以内的房屋,最多可以偷窃的金额为dp[i]。

  2. 确定递推公式
    决定dp[i]的因素就是第i房间偷还是不偷。

如果偷第i房间,那么dp[i] = dp[i - 2] + nums[i] ,即:第i-1房一定是不考虑的,找出 下标i-2(包括i-2)以内的房屋,最多可以偷窃的金额为dp[i-2] 加上第i房间偷到的钱。

如果不偷第i房间,那么dp[i] = dp[i - 1],即考 虑i-1房,(注意这里是考虑,并不是一定要偷i-1房,这是很多同学容易混淆的点)

然后dp[i]取最大值,即dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);

  1. dp数组如何初始化

从递推公式dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);可以看出,递推公式的基础就是dp[0] 和 dp[1]

从dp[i]的定义上来讲,dp[0] 一定是 nums[0],dp[1]就是nums[0]和nums[1]的最大值即:dp[1] = max(nums[0], nums[1]);

class Solution {
public:
    int rob(vector<int>& nums) {
        //1. dp数组的含义 是dp[i] 0到i,的最优选项,不一定包含当前节点
        vector<int> dp(nums.size(), 0);

        if(nums.size() == 0) return 0;
        if(nums.size() == 1) return nums[0];

        //2. 递推公式
        //当前节点的结果有两种情况一中是投当前节点,一种是不偷当前节点
        //偷当前节点就是,前一个节点的最优解:dp[i-1]
        //不偷当前节点就是前一个节点不能偷,所以是dp[i-2] + nums[i];
        //所以递推公式就是 dp[i] = max(dp[i-1], dp[i-2] + nums[i];
        
        //3.  初始化
        dp[0] = nums[0];
        dp[1] = max(nums[0], nums[1]);
        
        for(int i = 2; i < nums.size(); i++) {
            dp[i] = max(dp[i-1], dp[i-2] + nums[i]);
        }
        return dp[nums.size() -1];
    }
};

213.打家劫舍II

leetcode题目链接
视频讲解
文章讲解

将环形数组,根据题目描述可以,分解为两种数组

// 注意注释中的情况二情况三,以及把198.打家劫舍的代码抽离出来了
class Solution {
public:
    int rob(vector<int>& nums) {
        if (nums.size() == 0) return 0;
        if (nums.size() == 1) return nums[0];
        int result1 = robRange(nums, 0, nums.size() - 2); // 情况二
        int result2 = robRange(nums, 1, nums.size() - 1); // 情况三
        return max(result1, result2);
    }
    // 198.打家劫舍的逻辑
    int robRange(vector<int>& nums, int start, int end) {
        if (end == start) return nums[start];
        vector<int> dp(nums.size());
        dp[start] = nums[start];
        dp[start + 1] = max(nums[start], nums[start + 1]);
        for (int i = start + 2; i <= end; i++) {
            dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);
        }
        return dp[end];
    }
};

337.打家劫舍III

leetcode题目链接
视频讲解
文章讲解
动态规划结合树形结构,比较难

class Solution {
public:
    //int dp[2] = {0}; dp[0]  表示不偷当前节点,dp[1] 表示偷当前节点

    vector<int> robTree(TreeNode * node) {
        if(node == nullptr) return {0,0};
        vector<int> left(2,0);
        vector<int> right(2,0);
        left = robTree(node->left); //左
        right = robTree(node->right);//右

        // 中,后续遍历,已知左右的值推出当前节点的值
        //不偷当前节点就是要偷 左节点和有节点, 将左节点偷和不偷的最大值和右节点头或者不偷的最大值加一起
        int tmp1 = max(left[0], left[1]) + max(right[0], right[1]);
        //偷当前节点的话, 就是将当前节点的值加上,左节点和有节点都偷的和
        int tmp2 = node->val + left[0] + right[0];

        return {tmp1, tmp2};
    }
    int rob(TreeNode* root) {
        vector<int> vec(2,0);
        vec = robTree(root);
        //返回根节点偷或者不偷的最大值
        return max(vec[0], vec[1]);
    }
};

原文地址:https://blog.csdn.net/he979731102/article/details/136399855

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!