【LeetCode】18.动态规划系列——打家劫舍
总目录:
0.理论基础
0.1.要点
一小类问题,无专用理论。
1.打家劫舍
1.1.问题描述
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
链接:https://leetcode.cn/problems/house-robber
1.2.要点
dp[n],到第n处时能获得的最大价值
递推公式:分第n处取或者不取两种情况,所以dp[n]=max(dp[n-1],dp[n-2]+value[n])
1.3.代码实例
1 class Solution { 2 public: 3 int rob(vector<int>& nums) { 4 int len=nums.size(); 5 //dp[i][j],到第i家时能够获得的最大值,j=0不劫,j=1劫 6 vector<vector<int>> dp(len,vector<int>(2,0)); 7 dp[0][0]=0; 8 dp[0][1]=nums[0]; 9 for(int i=1;i<len;i++){ 10 dp[i][0]=max(dp[i-1][0],dp[i-1][1]); 11 dp[i][1]=dp[i-1][0]+nums[i]; 12 } 13 14 return max(dp[len-1][0],dp[len-1][1]); 15 } 16 };
2.打家劫舍——环形
2.1.问题描述
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。
给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。
链接:https://leetcode.cn/problems/house-robber-ii
2.2.要点
首尾不能同时出现,分别在[0~n-1]和[1~n]上进行动态规划,然后取二者的较大值
2.3.代码实例
1 class Solution { 2 public: 3 int rob(vector<int>& nums) { 4 int len=nums.size(); 5 if(len<=0){ 6 return 0; 7 } 8 if(len==1){ 9 return nums[0]; 10 } 11 if(len==2){ 12 return max(nums[0],nums[1]); 13 } 14 15 //不考虑尾 16 vector<int> dp(len,0);//dp[n]为打劫到第n家时可以获得的最大值 17 dp[0]=nums[0]; 18 dp[1]=max(nums[0],nums[1]); 19 for(int i=2;i<len-1;i++){ 20 dp[i]=max(dp[i-1],dp[i-2]+nums[i]); 21 } 22 int withoutTail=dp[len-2]; 23 24 //不考虑头 25 vector<int> dp2(len,0);//dp[n]为打劫到第n家时可以获得的最大值 26 dp2[0]=0; 27 dp2[1]=nums[1]; 28 dp2[2]=max(nums[1],nums[2]); 29 for(int i=3;i<len;i++){ 30 dp2[i]=max(dp2[i-1],dp2[i-2]+nums[i]); 31 } 32 int withoutHead=dp2[len-1]; 33 34 return max(withoutHead,withoutTail); 35 } 36 };
3.打家劫舍——树形
3.1.问题描述
小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root 。
除了 root 之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果 两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。
给定二叉树的 root 。返回 在不触动警报的情况下 ,小偷能够盗取的最高金额 。
链接:https://leetcode.cn/problems/house-robber-iii
3.2.要点
类似树形监控问题,要从底层开始遍历,因为底层数量是指数级的,采用后序遍历。
如果仅仅有后序遍历的递归,计算量较大可能会超时,需要用到动态规划
返回值是1个元组,[0]代表子树不取根节点时的最大值,[1]代表子树取根节点时的最大值。
对于本层节点,
不取之,则子树结果任选,[0]=max(left[0],left[1])+max(right[0],right[1])
取之,则子树的根节点不能取,[1]=cur->val+left[0]+right[0],
最后取根节点的[0]、[1]较大值即可。
3.3.代码实例
1 class Solution { 2 public: 3 vector<int> pick(TreeNode* root){ 4 if(root==NULL){ 5 return {0,0};//[0]不偷本层的最大收获,[1]偷本层的最大收获 6 } 7 vector<int> left=pick(root->left); 8 vector<int> right=pick(root->right); 9 10 //不偷本层,下层取较大的一个 11 int val1=max(left[0],left[1])+max(right[0],right[1]); 12 //偷本层 13 int val2 = root->val + left[0] + right[0]; 14 15 16 return {val1,val2}; 17 } 18 int rob(TreeNode* root) { 19 auto val= pick(root); 20 return max(val[0],val[1]); 21 } 22 };
xxx.问题
xxx.1.问题描述
111
xxx.2.要点
222
xxx.3.代码实例
333