代码随想录算法训练营第五十天 | 198.打家劫舍、213.打家劫舍II、337.打家劫舍III

198.打家劫舍

解题思路

1. dp[i]  考虑下标i(包含),能偷的最大金币是dp[i]

2.递推公式

 偷i:dp[i-2] + nums[i]   (i-2之前是考虑范围)

不偷i:dp[i-1] (i-1之前是考虑范围),不一定非要偷

dp[i] = max(上面两个)

3.初始化

dp[0] = nums[0];

dp[1] = max(nums[0],nums[1])  不一定非得偷1,偷这两个房间其中一个的最大值(根据定义)

非0初始为0

4.从小到大去遍历

class Solution {
public:
    int rob(vector<int>& nums) {
         if(nums.size()==1) return nums[0];
         if(nums.size()==2) return max(nums[0],nums[1]);
         vector<int> dp(nums.size(),0);
         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

解题思路

解决环的问题:

其实我们可以直接分为三种情况

情况1:不考虑首尾

情况2:考虑头不考虑尾

情况3:考虑尾不考虑头

情况2和情况3已经包含情况1了(仅仅是遍历的范围,并不一定要取)

class Solution {
private:
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],nums[start+1]);
         for(int i=start+2 ; i<=end ; i++)
         {
            dp[i] = max( dp[i-1] , dp[i-2]+nums[i]);
         }
         return dp[end];
    }
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);
    }
};

337.打家劫舍III

解题思路

树形DP

1.dp含义    dp[0]表示不偷的最大金钱,dp[1]表示偷的最大金钱,那么每个节点就会有一个vector<int> nodeDp{不偷,偷}

2. 返回当前节点偷或者不偷的最大金币数量的递归函数

vector<int> robtree(TreeNode* cur)
    {
        if(cur==nullptr) return vector<int>{0,0};   //空节点偷和不偷,价值都是0
        //后序遍历,先处理左右孩子,方便后面判断
        vector<int> leftdp = robtree(cur->left);
        vector<int> rightdp = robtree(cur->right);
        int val1 = cur->val + leftdp[0] + rightdp[0];  //偷当前节点,和左右孩子不偷的情况
        int val2 = max(leftdp[0],leftdp[1]) + max(right[0]+rightdp[1]); //左右孩子偷不偷要取一个最大的
        return {val2,val1};    //表示偷和不偷当前节点
    }

class Solution {
public:
    vector<int> robtree(TreeNode* cur)
    {
        if(cur==nullptr) return vector<int>{0,0};   //空节点偷和不偷,价值都是0
        //后序遍历,先处理左右孩子,方便后面判断
        vector<int> leftdp = robtree(cur->left);
        vector<int> rightdp = robtree(cur->right);
        int val1 = cur->val + leftdp[0] + rightdp[0];  //偷当前节点,和左右孩子不偷的情况
        int val2 = max(leftdp[0],leftdp[1]) + max(rightdp[0],rightdp[1]); //左右孩子偷不偷要取一个最大的
        return {val2,val1};    //表示偷和不偷当前节点
    }
    int rob(TreeNode* root) {
         vector<int> result = robtree(root);
         return max(result[0],result[1]);
    }
};
  • 时间复杂度:O(n),每个节点只遍历了一次
  • 空间复杂度:O(log n),算上递推系统栈的空间

 收获

有点难

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值