代码随想录第48天 | 198.打家劫舍 、 213.打家劫舍II 、 337.打家劫舍III

一、前言

参考文献:代码随想录

今天的主题是打家劫舍,动态规划;

首先回忆动态规划的步骤:

(1)创建dp数组;

(2)确定递推公式;

(3)初始化;

(4)遍历顺序;

(5)打印dp数组;

二、打家劫舍

1、思路:

我一开始的思路是打算用背包思路做的,,,发现现在已经被背包洗脑了。。

越做越麻烦,然后就看了卡哥的文章,豁然开朗;

首先我复述一下最重要的递推公式:

(1)房子分为两种状态:被偷、不被偷;

当被偷时,它的邻接就不会被偷了,当不被偷是他的邻居可能更富

所以我们围绕这个来创建了一个dp数组dp[i]:在第i家屋子所能获得的最大财物为dp[i];

当我们偷当前这家,那么我们邻居就不能再偷了:dp[i -2] + nums[i](价值);

如果我们不偷这一家,那么上一家就会被偷: dp[i - 1];

对,就是这样;

(2)然后就是初始化了:

我们发现dp[i]是有前面推出来的(遍历顺序)所以我们需要初始化前两个值;

dp[0]很显然等于nums[0],那dp[1]呢?

那就是nums[0] and nums[1]之间的最大值啦;

2、整体代码如下:

class Solution {
public:
    int rob(vector<int>& nums) {
        if (nums.size() == 1) return nums[0];
        if (nums.size() == 0) return 0;
        // 1、创建dp数组
        vector<int> dp(nums.size(), 0);
        // 2、初始化
        dp[0] = nums[0];
        dp[1] = max(nums[0], nums[1]);

        // 3、遍历顺序
        for (int i = 2; i < nums.size(); i++) {
            // 4、递推公式
            // 分为这个房间偷还是不投,取最大值
            dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);
        }   
        
        // 5、打印dp数组
        for (auto i : dp) {
            cout << i << " ";
        }
        cout << endl;


        return dp[nums.size() - 1];

    }
};

三、打家劫舍||

1、思路:

与上一题差不多,但是需要分为三种情况:

(1)不包含头部和尾部:那就和198如出一辙了。

(2)包含头部,不包含尾部,那也可以用198的思路去做,就不会成环;

(3)包含尾部,不包含头部,也是198的思路。

第一种情况被第二三种情况包含了,所以只需要做两种情况就可以了。可以直接通过指针来确定头和尾。

2、整体代码如下:

class Solution {
    
public:
    int rob(vector<int>& nums) {
        int size = nums.size();
        if (size == 1) return nums[0];
        if (size == 0) return 0;
        int max2 = robRange(nums, 0, size - 2);
        int max3 = robRange(nums, 1, size - 1);

        return max(max2, max3);

    }
    int robRange(vector<int>& nums, int start, int end) {
        if (end == start) return nums[end];
        vector<int> dp(nums.size(), 0);
        dp[start] = nums[start];
        dp[start + 1] = max(nums[start + 1], nums[start]);
        
        for (int i = start + 2; i <= end; i++) {
            dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);
        }
        return dp[end];
    }
};

四、打家劫舍|||

1、思路:

比较抽象,然是掌握了树形思维方式的话,就可以解决了;

(1)首先本题使用的是递归+dp来解决的,在递归中穿插着dp。首先是dp数组,其实这里的dp数组与其说是dp数组,更可以把他看作一个标记:分为dp[2],0代表不偷该节点,1代表偷该节点;每一层递归穿插着这样的dp数组,他的值就是有没有偷该节点的值。

(2)其次就是递归三部曲了:

1-参数和返回值:

    vector<int> robTree(TreeNode* cur)

这里就是返回的是dp数组,参数不用多说了;

2-终止条件:

if (cur == NULL) return vector<int>{0, 0};

遍历到空节点就返回了,返回的就是没有价值的dp;

3-遍历顺序就是后续遍历,从叶子节点开始遍历,才能够知道孩子节点有没有值,从而推到根节点的大小,判断根节点取还是不取:

        // 左
        vector<int> leftdp = robTree(cur->left);
        // 右
        vector<int> rightdp = robTree(cur->right);
        // 中
        int val_get = cur->val + leftdp[0] + rightdp[0];
        int val_lose = max(leftdp[0], leftdp[1]) + max(rightdp[0], rightdp[1]);
        return vector<int> {val_lose, val_get};

2、整体代码如下:

class Solution {
private:
    vector<int> robTree(TreeNode* cur) {
        // 这里的dp表示的是0为偷,1为不偷
        // 终止条件
        if (cur == NULL) return vector<int>{0, 0};
        // 左
        vector<int> leftdp = robTree(cur->left);
        // 右
        vector<int> rightdp = robTree(cur->right);
        // 中
        int val_get = cur->val + leftdp[0] + rightdp[0];
        int val_lose = max(leftdp[0], leftdp[1]) + max(rightdp[0], rightdp[1]);
        return vector<int> {val_lose, val_get};
    }
public:
    int rob(TreeNode* root) {
        // 看完卡哥的讲解,一个字:绝顶
        vector<int> dp = robTree(root);
        return max(dp[0], dp[1]);
    } 
};

 Time to Study: 2h.

leave message:

Don't judge each day by the harvest you reap but by the seeds taht you plant.

 不要以收获来评判每一天,而要以你播下的种子来评判。

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值