代码随想录–动态规划部分
day 40 动态规划第7天
一、力扣198–打家劫舍
代码随想录题目链接:代码随想录
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
问题背景确实挺抽象的
实际上就是不能连着偷,最多也只能隔一个偷一家
dp[j]
就可以认为是第j
家能够偷到的最大金额
这个和什么有关系呢?要看dp[j-2]
加上第j
家的钱多不多,如果比dp[j-1]
多,那肯定就是选择偷了
如果不如dp[j-1]
多,那第j
家就不偷了,此时有dp[j]=dp[j-1]
,因为没偷所以保持一致
所以递归公式为:dp[i] = max(dp[i-2] + nums[i], dp[i-1])
明显i要大于1,所以dp[0] = nums[0], dp[1] = max(nums[0], nums[1])
初始化结束,直接写
代码如下:
class Solution {
public:
int rob(vector<int>& nums) {
if (nums.size() == 0) return 0;
if (nums.size() == 1) return nums[0];
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-2] + nums[i], dp[i-1]);
return dp[nums.size() - 1];
}
};
二、力扣213–打家劫舍Ⅱ
代码随想录题目链接:代码随想录
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。
给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。
这次是成环了,真去处理环还有点麻烦的
所以拆了,按照以下两种方法拆了,这样就可以不用考虑成环的相邻问题
那么对这两种情况分别求一次dp,取两者更佳的输出即可
子函数就是上题解法,一模一样的
代码如下:
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);
}
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–打家劫舍Ⅲ
代码随想录题目链接:代码随想录
小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root 。
除了 root 之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果 两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。
给定二叉树的 root 。返回 在不触动警报的情况下 ,小偷能够盗取的最高金额 。
把打家劫舍的数组变成了树,那么遍历方式就有的讲了
最直接的做法是遍历树,构建数组,再对数组进行动态规划
但是没法这么做,不管前中后序遍历都不能使数组的相邻关系满足树节点的满足关系
所以需要结合树结构进行遍历,那么这样就会陷入递归当中,dp不太好记录全局的状态
所以dp实际上只需要记录当前状态即可
而遍历每个节点时,都只有偷与不偷两个状态,那么dp定义为长度2的数组,记录偷与不偷的结果即可
递归逻辑?因为父节点和子节点不能同时偷,所以要比较两个数:父√子× 和 父×子√
也就对应着偷本节点 和 不偷本节点两个结果,组成dp数组返回上一层递归即可
这样返回到最初的递归入口后,就能得到最高金额了
代码如下:
class Solution {
public:
int rob(TreeNode* root) {
vector<int> result = travrsel(root);
return max(result[0], result[1]);
}
vector<int> travrsel(TreeNode * curr)
{
// dp[0] dont steal, dp[1] do steal
if(!curr) return {0, 0};
vector<int> left = travrsel(curr->left);
vector<int> right = travrsel(curr->right);
int yesIdo = curr->val + left[0] + right[0];
int noIdont = max(left[0], left[1]) + max(right[0], right[1]);
return {noIdont, yesIdo};
}
};