LeetCode刷题day30——动态规划(打家劫舍)
上一次刷题是2024-12-20,今天是2025-1-7,刚考完期末考试,元旦节出去玩了几天,回来躺了两天,就是现在了。时间过得好快,有很多要做的事情,好像今天才开始找到学习的状态,也挺好的。我的预想是上午打题,下午学技术,晚上搞点科研,跑步。每天玩手机控制时间。大概是这样安排的,如果能坚持下来就好了。生命的其中一部分意义是给时间投资。
198. 打家劫舍
已解答
中等
相关标签
相关企业
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例 1:
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 2:
输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。
提示:
1 <= nums.length <= 100
0 <= nums[i] <= 400
分析:
dp[i]:考虑下标i(包括i)以内的房屋,最多可以偷窃的金额为dp[i]。
dp[i]=max(dp[i-1],dp[i-2]+nums[i]);
dp[i-1]
第前一个被偷,这个就不偷
dp[i-2]+nums[i]
第前二个被偷,前一个没被偷,这个就偷
class Solution {
public:
int rob(vector<int>& nums) {
vector<int> dp(nums.size(), 0);
if(nums.size()==1)
return nums[0];
dp[0]=nums[0];
dp[1]=max(nums[1],nums[0]);
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:
输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
示例 2:
输入:nums = [1,2,3,1]
输出:4
解释:你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 3:
输入:nums = [1,2,3]
输出:3
提示:
1 <= nums.length <= 100
0 <= nums[i] <= 1000
分析:
思路是:第一个不偷或者最后一个不偷
class Solution {
public:
int rob(vector<int>& nums) {
//代码写得依托答辩,总之思路是:第一个不偷或者最后一个不偷
vector<int> dp(nums.size(), 0);
if (nums.size() == 1)
return nums[0];
dp[0] = nums[0];
dp[1] = dp[0];
// not steal last
int sum1 = dp[1];
for (int i = 2; i < nums.size() - 1; i++) {
dp[i] = max(dp[i - 1], dp[i - 2] + nums[i]);
sum1 = max(sum1, dp[i]);
}
vector<int> dp2(nums.size(), 0);
dp2[1] = nums[1];
dp2[0] = 0;
int sum2 = dp2[1];
for (int i = 2; i < nums.size(); i++) {
dp2[i] = max(dp2[i - 1], dp2[i - 2] + nums[i]);
sum2 = max(sum2, dp2[i]);
}
return max(sum1, sum2);
}
};
参考了别人的代码,果然写的标准,自己也跟着实现了一遍:
class Solution {
public:
//抽离出上一题的逻辑,分区间写
int rob_zone(const vector<int>& nums, int start, int end) {
vector<int> dp(nums.size(), 0);
if (start == end)
return nums[start];
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 - 1], dp[i - 2] + nums[i]);
return dp[end];
}
int rob(vector<int>& nums) {
int len = nums.size();
if (len == 0)
return 0;
if (len == 1)
return nums[0];
int exp_last = rob_zone(nums, 0, len - 2);
int exp_head = rob_zone(nums, 1, len - 1);
return max(exp_last, exp_head);
}
};
337. 打家劫舍 III
已解答
中等
相关标签
相关企业
小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root
。
除了 root
之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果 两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。
给定二叉树的 root
。返回 在不触动警报的情况下 ,小偷能够盗取的最高金额 。
示例 1:
输入: root = [3,2,3,null,3,null,1]
输出: 7
解释: 小偷一晚能够盗取的最高金额 3 + 3 + 1 = 7
示例 2:
输入: root = [3,4,5,1,3,null,1]
输出: 9
解释: 小偷一晚能够盗取的最高金额 4 + 5 = 9
提示:
-
树的节点数在
[1, 104]
范围内 -
0 <= Node.val <= 104
分析:
总而言之,这种树形dp还是蛮新颖,随之就是我不会这个题,不会还是要打对吧?
思路:
记录两个状态:此节点不偷的最大价值,此节点偷的最大价值(以此节点为父节点的子树中)。我们采用后序遍历,因为要通过左右子树的状态,来确定此节点的状态。
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), * right(right) {} * }; */ class Solution { public: int rob(TreeNode* root) { vector<int> res = robTree(root); // res[0],不偷; res[1],偷当前 return max(res[0], res[1]); } vector<int> robTree(TreeNode* node) { if (node == nullptr) return vector<int>{0, 0}; vector<int> left = robTree(node->left); vector<int> right = robTree(node->right); // 偷当前的,意味着左右孩子不偷 int steal_cur = node->val + left[0] + right[0]; // 不偷当前的,意味着左右孩子“可以偷”,但是却不一定会选择偷,还是要看效益最大 int not_steal_cur = max(left[1], left[0]) + max(right[1], right[0]); return vector<int>{not_steal_cur, steal_cur}; } };