198.打家劫舍
题解:本题依旧是是用动态规划法,动态规划五部曲
(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};
}
};