刷题记录
- *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]);}
};