198.打家劫舍
链接:代码随想录
这里我还以为要用01背包。。。。
用的是逻辑而不是套路。让人想起爬楼梯那道题。这里我犯了初始化错误,错误在注释中写出。
正确做法:
class Solution { /* 比作跳台阶的话,这个只能一次跳两个台阶的感觉. 1、每个元素只能使用1次,所以0、1背包 2、 dp[j]代表从0---j房间能偷到的最大金额(包括j) dp[j]=max(dp[j-1],dp[j-2]+nums[i]) */ public: int rob(vector<int>& nums) { int n=nums.size(); if (n == 0) return 0; if (n== 1) return nums[0]; vector<int>dp(n,0); /* 初始化错了,因为我们算的是闭区间,是包含当前的nums[j]的 dp[0]=0; dp[1]=nums[0];*/ //dp[0] 一定是 nums[0],dp[1]就是nums[0]和nums[1]的最大值即:dp[1] = max(nums[0], nums[1]); dp[0]=nums[0]; dp[1]=max(nums[0],nums[1]); for(int j=2;j<n;j++) { dp[j]=max(dp[j-1],dp[j-2]+nums[j]); } return dp[n-1]; } };
没想到做法,感觉这道题只能靠认识,自己想不出来。
看着懵,暂时不想做
337.打家劫舍 III
链接:代码随想录
视频链接:动态规划,房间连成树了,偷不偷呢?| LeetCode:337.打家劫舍3_哔哩哔哩_bilibili
![]()
![]()
啥也不会,但是视频讲的太好啦!!! 树形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) {} * }; */ /* 树形dp的入门题目,因为是在树上进行状态转移,我们在讲解二叉树的时候说过递归三部曲,那么下面我以递归三部曲为框架,其中融合动规五部曲的内容来进行讲解。 对于每个节点,都有偷和不偷两种状态,要记录每个节点偷时的最大值和不偷时的最大值。 所以对于每个节点,都有滚动数组vector<int>dp(2,0); dp[0]代表该节点不偷的情况下最大值; dp[1]代表该节点偷的情况下最大值; 后序遍历,从下往上遍历。则对于本状态,下一层是上一个状态。 2、递推公式 对于一个节点, 如果他被偷,则上一个状态(在这里从下往上,是左右节点)必然不能被偷, 本节点node的dp[1]=node->val+nodeleft的dp[0]+noderight的dp[0]; 如果他不被偷,则上一个状态可以被偷也可以不被偷,只需要看哪个大。而且左右节点必被相加 本节点node的dp[0]=max(nodeleft的dp[0],nodeleft的dp[1])+ max(noderight的dp[0],noderight的dp[1]) 3、初始值 后序遍历,从下往上遍历。则对于示例1的叶子节点,{0,3} */ class Solution { public: int rob(TreeNode* root) { vector<int>dp= dfs(root); return max(dp[0],dp[1]); } vector<int> dfs(TreeNode *root) { if(root==nullptr) { return {0,0}; } vector<int>left=dfs(root->left); vector<int>right=dfs(root->right); //本节点不被偷 int val_not_rob=max(left[0],left[1])+max(right[0],right[1]); //本节点被偷,则上一个状态必定不被偷 int val_rob=root->val+left[0]+right[0]; return {val_not_rob,val_rob}; } };