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