刷题记录
- *56. 携带矿石资源(第八期模拟笔试)
- 198. 打家劫舍
- 213. 打家劫舍 II
- *337. 打家劫舍 III
- 解法一 (记忆化递推)
- *解法二 (动态规划)
 
*56. 携带矿石资源(第八期模拟笔试)
leetcode题目地址
多重背包问题可以拆解成01背包问题。
时间复杂度:  
     
      
       
       
         O 
        
       
         ( 
        
       
         m 
        
       
         ∗ 
        
       
         n 
        
       
         ∗ 
        
       
         k 
        
       
         ) 
        
       
      
        O(m*n*k) 
       
      
    O(m∗n∗k)
 空间复杂度:  
     
      
       
       
         O 
        
       
         ( 
        
       
         n 
        
       
         ) 
        
       
      
        O(n) 
       
      
    O(n)
// c++
#include<bits/stdc++.h>
using namespace std;
int main(){
    int c,n;
    cin>>c>>n;
    
    vector<int> weight(n, 0);
    vector<int> value(n, 0);
    vector<int> nums(n, 0);
    vector<int> dp(c+1, 0);
    for (int i = 0; i < n; i++) cin >> weight[i];
    for (int i = 0; i < n; i++) cin >> value[i];
    for (int i = 0; i < n; i++) cin >> nums[i];
    
    for(int i=0; i<n; i++){
        for(int j=c; j>=weight[i]; j--){
            for(int k=1; k<=nums[i]&&(j-k*weight[i]>=0); k++){
                dp[j] = max(dp[j], dp[j-k*weight[i]]+k*value[i]);
            }
        }
    }
    std::cout << dp[c] << std::endl;
    return   0;                                                                 
}
198. 打家劫舍
leetcode题目地址
dp[i]存储到第i个房屋时的最大价值。
每个房屋有两个状态:取或不取。若取,则当前房屋的最大价值是dp[i-2]+nums[i];若不取,则当前房屋的最大价值是前一个房屋的最大价值。因此状态转移方程为: d p [ i ] = m a x ( d p [ i − 2 ] + n u m s [ i ] , d p [ i − 1 ] ) dp[i] = max(dp[i-2]+nums[i], dp[i-1]) dp[i]=max(dp[i−2]+nums[i],dp[i−1])
时间复杂度:  
     
      
       
       
         O 
        
       
         ( 
        
       
         n 
        
       
         ) 
        
       
      
        O(n) 
       
      
    O(n)
 空间复杂度:  
     
      
       
       
         O 
        
       
         ( 
        
       
         n 
        
       
         ) 
        
       
      
        O(n) 
       
      
    O(n)
// c++
class Solution {
public:
    int rob(vector<int>& nums) {
        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. 打家劫舍 II
leetcode题目地址
使用两个dp数组分别记录取头不取尾和取尾不取头。其他逻辑和上一题一致。
时间复杂度:  
     
      
       
       
         O 
        
       
         ( 
        
       
         n 
        
       
         ) 
        
       
      
        O(n) 
       
      
    O(n)
 空间复杂度:  
     
      
       
       
         O 
        
       
         ( 
        
       
         n 
        
       
         ) 
        
       
      
        O(n) 
       
      
    O(n)
// c++
class Solution {
public:
    int rob(vector<int>& nums) {
        if(nums.size()==1) return nums[0];
        // 取nums[0]
        vector<int> dp(nums.size(), 0);
        // 不取nums[0]
        vector<int> dp1(nums.size(), 0);
        dp[0] = nums[0];
        dp[1] = max(nums[0], nums[1]);
        dp1[1] = nums[1];
        for(int i=2; i<nums.size()-1; i++){
            dp[i] = max(dp[i-2]+nums[i], dp[i-1]);
        }
        
        for(int i=2; i<nums.size(); i++){
            dp1[i] = max(dp1[i-2]+nums[i], dp1[i-1]);
        }
        
        return max(dp[nums.size()-2],dp1[nums.size()-1]);
    }
};
*337. 打家劫舍 III
leetcode题目地址
解法一 (记忆化递推)
使用二叉树的后序遍历来求是否加入当前节点,使用一个map来记录已经求过的结点值。
时间复杂度:  
     
      
       
       
         O 
        
       
         ( 
        
       
         n 
        
       
         ) 
        
       
      
        O(n) 
       
      
    O(n)
 空间复杂度:  
     
      
       
       
         O 
        
       
         ( 
        
       
         l 
        
       
         o 
        
       
         g 
        
       
         n 
        
       
         ) 
        
       
      
        O(logn) 
       
      
    O(logn)
// c++
/**
 * 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:
    // 记录计算过的结果,保证每个结点只计算一次
    unordered_map<TreeNode*, int> umap;
    int rob(TreeNode* root) {
        if(!root) return 0;
        if(!root->left && !root->right) return root->val;
        // 当前节点已计算过,直接返回
        if(umap[root]) return umap[root];
        // 取根
        int val1 = root->val;
        if(root->left) val1 += rob(root->left->left) + rob(root->left->right);
        if(root->right) val1 += rob(root->right->left) + rob(root->right->right);
        // 不取根
        int val2 = rob(root->left) + rob(root->right);
        // 记录当前结果
        umap[root] = max(val1,val2); 
        return max(val1,val2); 
        // return 0;
    }
};
*解法二 (动态规划)
使用二叉树的后序遍历来计算左右孩子节点的取与不取的价值,再计算当前节点的取与不取,返回结果。
思路
时间复杂度:  
     
      
       
       
         O 
        
       
         ( 
        
       
         n 
        
       
         ) 
        
       
      
        O(n) 
       
      
    O(n)
 空间复杂度:  
     
      
       
       
         O 
        
       
         ( 
        
       
         l 
        
       
         o 
        
       
         g 
        
       
         n 
        
       
         ) 
        
       
      
        O(logn) 
       
      
    O(logn)
// c++ 
/**
 * 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:
    vector<int> robTree(TreeNode* root){
        if(!root) return {0, 0};
        vector<int> left = robTree(root->left);
        vector<int> right = robTree(root->right);
        // 偷当前节点,孩子节点不可偷
        int val1 = root->val + left[0] + right[0];
        // 不偷当前节点,孩子节点可偷可不偷
        int val2 = max(left[0], left[1]) + max(right[0], right[1]);
        return {val2, val1};
    }
    int rob(TreeNode* root) {
        vector<int> res = robTree(root);
        return max(res[0], res[1]);
    }
};


















