Day48:代码随想录训练营第48天:打家劫舍系列

198.打家劫舍

题目链接:198. 打家劫舍 - 力扣(LeetCode)

题解:本题依旧是是用动态规划法,动态规划五部曲

(1):确定dp数组以及下标含义

dp[i]:考虑下标i并所能偷盗的最大钱币数量为dp[i]

(2):确定递推公式

在面对该题主要有两种,偷和不偷,那么先讨论第一种情况偷

1.在偷的时候要明白不能偷相邻的房间,所以是dp[i - 2],这是偷第i个以及前i个房间的钱币及dp[i - 2]+nums[i]。

2.第二种不同情况是不偷,那么递推式就是dp[i - 1]。

那么总和两种情况最后所得到的递推公式就是:dp[i] = max(dp[i - 2]+nums[i],dp[i - 1]);

(3):dp数组初始化

dp[0]:那么nums[0]一定为0

dp[1]:偷只能偷第i个或者是前一个,所以只能是偷其中一个

(4):确定遍历顺序

有递推公式可以看出就是一种从小到大的遍历

(5):打印dp数组

C++代码如下:

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

       }
};

213.打家劫舍II

题目链接: 213. 打家劫舍 II - 力扣(LeetCode)

 题解:

本题与上述198.打家劫舍几乎相同,主要还是运用动态规划法,动态规划五部曲,但本题的变种就在于这是一个成环数组,那么题目要求是不能偷取相邻的房间,所以你只可以去偷首和尾的房间,所以会有以下三种情况。

(1):不考虑首尾元素

(2):只考虑首元素

(3):只考虑尾元素

但第一种情况是包含在后两种情况里的,所以最终还是只考虑后两种情况,因为这是考虑,并非一定要去偷最后的尾元素或者首元素。

那么动规五部曲还是和上题一样。

C++代码如下:

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

//数组成环
       int robRange(b=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

题目链接:337. 打家劫舍 III - 力扣(LeetCode)

题解:

本题使用动态规划,但与之前的题目还是有所不同,这是一种关于树的遍历,那么既然涉及到树的相关知识,就一定要考虑其中一种遍历方式(前中后遍历或是层次遍历)那么 这题是一种后序遍历,根据递推公式来推导,所以接下来是以递归三部曲为框架,然后结合动态规划五部曲来写。

(1):确定递推函数参数和返回值

设计一个节点,偷和不偷会有两种状态钱币会有两种金钱

vector<int> robTree(TreeNode * cur) //设置当前参数节点

dp数组的含义:下标为0记录不偷该节点所得到的最大金币,下标为1记录偷该节点所得到的最大金钱。

(2):确定终止条件

在遍历的过程中,如果遇到空节点,那么在面对偷还是不偷都是0,所以就返回

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

相当于dp数组的初始化

(3):确定遍历顺序

首先明确使用后序遍历,因为要通过函数做下一步计算

通过递归左节点,得到左节点偷不偷的金币

通过递归右节点,得到右节点偷不偷的金币
(4):确定单层递归的逻辑

有两种情况,如果偷当前节点,那么该节点的左右节点就不能偷,val =  cur ->val + left[0] + right[0]

如果不偷当前节点,那么该节点的左右节点就能偷,val = max(left[0],left[1]) + max(right[0], right[1]);(选两个节点的最大值)

vector<int> left = robTree(cur->left); 
vector<int> right = robTree(cur->right);


//偷该节点
int val1 = cur->val + left[0] + right[0];
//不偷该节点
int val2 = max(left[0], left[1]) + max(right[0], right[1]);
return {val2,val1};

5.打印dp数组

class Solution {
public:
    int rob(TreeNode* root) {
       vector<int> result = robTree(root);//封装树的函数
       return max(result[0], result[1]);//返回所得到最大的金币数
}
    vector<int> robTree(TreeNode* cur) {
            if (cur == NULL) return vector<int>{0,0};//当前该节点为0时
           //分为两种情况:偷和不偷
           vector<int> left = robTree(cur -> left);
           vector<int> right = robTree(cur -> right);
           //偷当前该节点
           int val1 = cur->val + left[0] + right[0];
          //不偷该节点
           int val2 = max(left[0], left[1]) + max(right[0], right[1]);
           return {val1, val2};
      }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值