198. House Robber; 213. House Robber II; 337. House Robber III

参考:经典动态规划:打家劫舍系列问题

问题:

base题目:

给定一组数列,代表一排住宅,各自能偷出的钱数。

不能偷取相邻的两家。求能偷出的最大钱数。

 

  • I base题目
  • Example 1:
    Input: nums = [1,2,3,1]
    Output: 4
    Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3).
                 Total amount you can rob = 1 + 3 = 4.
    
    Example 2:
    Input: nums = [2,7,9,3,1]
    Output: 12
    Explanation: Rob house 1 (money = 2), rob house 3 (money = 9) and rob house 5 (money = 1).
                 Total amount you can rob = 2 + 9 + 1 = 12.
     
    Constraints:
    0 <= nums.length <= 100
    0 <= nums[i] <= 400
    

      

  • II base题目+首尾两家也互为相邻(住宅为环形,首尾相连)
  • Example 1:
    Input: [2,3,2]
    Output: 3
    Explanation: You cannot rob house 1 (money = 2) and then rob house 3 (money = 2),
                 because they are adjacent houses.
    
    Example 2:
    Input: [1,2,3,1]
    Output: 4
    Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3).
                 Total amount you can rob = 1 + 3 = 4.
    

      

  • III base题目+住宅为:先序遍历的二叉树(二叉树相连的两个节点互为相邻,不能同时偷取)
  • Example 1:
    Input: [3,2,3,null,3,null,1]
    
         3
        / \
       2   3
        \   \ 
         3   1
    
    Output: 7 
    Explanation: Maximum amount of money the thief can rob = 3 + 3 + 1 = 7.
    
    Example 2:
    Input: [3,4,5,1,3,null,1]
    
         3
        / \
       4   5
      / \   \ 
     1   3   1
    
    Output: 9
    Explanation: Maximum amount of money the thief can rob = 4 + 5 = 9.
    

      

 

解法:

I base题目:DP(动态规划)

1.确定【状态】:

  • 到当前为止的住宅:第 i 家

2.确定【选择】:

  • 选择 1(偷取 当前的第 i 家):
    • dp[i-2] + nums[i] :不能偷取相邻的前一家,只能从上上一家为止中进行选择 + 偷取当前家的钱数。
  • 选择 2 (不偷取 当前的第 i 家):
    • dp[i-1]:可以偷取相邻的前一家,从到前一家为止中进行选择

3. dp[i]的含义:

到第i家为止,能偷取获得的最大钱数。

4. 状态转移:

dp[i]= MAX {

  • 选择 1 (偷取 当前的第 i 家):dp[i-2] + nums[i]
  • 选择 2 (不偷取 当前的第 i 家):dp[i-1]

}

5. base case:

  • i==0:没有一家的住宅,最多能偷取钱数为0
    • dp[0] = 0
  • i==1:只有一家的住宅,最多即能偷取这一家,钱数为nums[0]
    • dp[1] = nums[0]

代码参考:

 1 class Solution {
 2 public:
 3     //dp[i]: in first i houses, the max money we can get
 4     //     = max(case_1, case_2)
 5     //case_1: choose i-th house
 6     //       dp[i-2] + nums[i]
 7     //case_2: not choose i-th house
 8     //       dp[i-1]
 9     //base case: 
10     // dp[0]=0
11     // dp[1]=nums[0]
12     int rob(vector<int>& nums) {
13         int n = nums.size();
14         if(n<=0) return 0;
15         vector<int> dp(n+1, 0);
16         dp[1] = nums[0];
17         for(int i=2; i<=n; i++) {
18             dp[i] = max(dp[i-2]+nums[i-1], dp[i-1]);
19         }
20         return dp[n];
21     }
22 };

♻️ 优化:

空间复杂度:2维->1维

去掉 i 

需要前一次的dp[1] ->dp_pre0

还有前前一次的dp[0]  ->dp_pre1

代码参考:

 1 class Solution {
 2 public:
 3     int rob(vector<int>& nums) {
 4         int n = nums.size();
 5         if(n<=0) return 0;
 6         int dp_pre0=nums[0], dp_pre1=0;
 7         for(int i=2; i<=n; i++) {
 8             int tmp = dp_pre0;
 9             dp_pre0 = max(dp_pre1+nums[i-1], dp_pre0);
10             dp_pre1 = tmp;
11         }
12         return dp_pre0;
13     }
14 };

 

 

II base题目+首尾两家也互为相邻(住宅为环形,首尾相连)

第一题 DP的基础上,

 

由于 情况一 包含在 情况二 or 情况三 中,因此只需要求得:MAX{ 情况二 , 情况三 }

分别对 情况二 情况三,带入第一题的DP解法中。

求nums[0~n-1] 和 nums[1~n] 的最大值,在取这二者的最大值即可。

代码参考:

 1 class Solution {
 2 public:
 3     int rob(vector<int>& nums) {
 4         int n = nums.size();
 5         if(n==1) return nums[0];
 6         return max(robdp(nums, 0, n-1), robdp(nums, 1, n));
 7     }
 8     int robdp(vector<int>& nums, int start, int end) {//[start,end)
 9         int n = end-start;
10         if(n<=0) return 0;
11         int dp_pre0=nums[start], dp_pre1=0;
12         for(int i=start+2; i<=end; i++) {
13             int tmp = dp_pre0;
14             dp_pre0 = max(dp_pre1+nums[i-1], dp_pre0);
15             dp_pre1 = tmp;
16         }
17         return dp_pre0;
18     }
19 };

 

III base题目+住宅为:先序遍历的二叉树(二叉树相连的两个节点互为相邻,不能同时偷取)

 dp[root][r]:对节点root来说,有两种选择:r=0(选择不偷取)的结果,or r=1(选择偷取)的结果。

要求的最终结果为:max{ dp[root][0], dp[root][1] }

而对root来说:

  • 若r=0(选择不偷取):max { dp[left][0], dp[left][1] } + max { dp[right][0], dp[right][1] }  左边右边都可以选择偷取or不偷取,分别取最大,然后相加。
  • 若r=1(选择偷取的结果):dp[left][0] + dp[right][0] 左边右边都只能不偷取,相加。

base case: root=null

  • dp[root][0] = 0
  • dp[root][1] = 0

代码参考:

 1 /**
 2  * Definition for a binary tree node.
 3  * struct TreeNode {
 4  *     int val;
 5  *     TreeNode *left;
 6  *     TreeNode *right;
 7  *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 8  *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 9  *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
10  * };
11  */
12 class Solution {
13 public:
14     //dp[root]=
15     //case_1:rob: dp[root][1]=
16     //       dp[left][0]+dp[right][0]+root->val
17     //case_2:not_rob: dp[root][0]=
18     //       max(dp[left][1],dp[left][0])
19     //     + max(dp[right][1],dp[right][1])
20     int rob(TreeNode* root) {
21         if(!root) return 0;
22         int* robres=dp(root);
23         return max(robres[0], robres[1]);
24     }
25     int* dp(TreeNode* root) {
26         int *robres = new int[2]{0,0};
27         if(!root) return robres;
28         int *left = dp(root->left);
29         int *right = dp(root->right);
30         robres[0] = max(left[1], left[0]) + max(right[1], right[0]);
31         robres[1] = left[0] + right[0] + root->val;
32         delete [] left;
33         delete [] right;
34         return robres;
35     }
36 };

 

posted @ 2020-09-18 10:48  habibah_chang  阅读(219)  评论(0编辑  收藏  举报