class Solution {
public:
int rob(vector<int>& nums) {
//dp[i]表示0~i个房间能够偷窃到的最高金额
//dp[i] = max(dp[i-2]+nums[i],dp[i-1]);
int len = nums.size();
if (len == 0)return 0;
if (len == 1)return nums[0];
vector<int>dp(len, 0);
dp[0] = nums[0];
dp[1] = max(nums[0], nums[1]);
for (int i = 2; i < len; i++)
{
dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);
}
return dp[len - 1];
}
};
还可以把空间复杂度降到O(1):
class Solution {
public:
int rob(vector<int>& nums) {
int n = nums.size();
if (!n) return 0;
if (n == 1) return nums[0];
if (n == 2) return max(nums[0], nums[1]);
int tmp, pre = 0, cur = 0;
for (int i = 0; i < n; i++) {
tmp = cur;
cur = max(pre + nums[i], cur);
pre = tmp;
}
return cur;
}
};
首先,首、尾房间不能同时被抢,那么只可能有两种不同情况:要么最后一间不抢;要么第一间不抢。
class Solution {
public:
int rob(vector<int>& nums) {
int len = nums.size();
if (len==0)
return 0;
if (len == 1)
return nums[0];
vector<int> dp1(len, 0);//dp1[i]表示0~i个房间能够偷窃到的最高金额(不抢第一个)
vector<int> dp2(len, 0);//dp2[i]表示0~i个房间能够偷窃到的最高金额(不抢最后一个)
dp1[0] = 0, dp1[1] = nums[1], dp2[0] = nums[0], dp2[1] = max(nums[0], nums[1]);
for (int i = 2; i < len; ++i) {
dp1[i] = max(dp1[i - 1], nums[i] + dp1[i - 2]);
}
for (int i = 2; i < len - 1; ++i) {
dp2[i] = max(dp2[i - 1], nums[i] + dp2[i - 2]);
}
return max(dp1[len - 1], dp2[len - 2]);
}
};
还可以把空间复杂度降到O(1):
class Solution {
public:
int rob(vector<int>& nums) {
int n = nums.size();
if (!n) return 0;
if (n == 1) return nums[0];
if (n == 2) return max(nums[0], nums[1]);
int tmp, pre = 0, cur = 0;
for (int i = 1; i < n; i++) {
tmp = cur;
cur = max(pre + nums[i], cur);
pre = tmp;
}
int res1 = cur;
pre = 0;
cur = 0;
for (int i = 0; i < n - 1; i++) {
tmp = cur;
cur = max(pre + nums[i], cur);
pre = tmp;
}
int res2 = cur;
return max(res1, res2);
}
};
思路1:
class Solution {
//当前结点选择不偷: 当前结点能偷到的最大钱数 = 左孩子能偷到的钱 + 右孩子能偷到的钱
//当前结点选择偷: 当前结点能偷到的最大钱数 = 左孩子选择自己不偷时能得到的钱 + 右孩子选择不偷时能得到的钱 + 当前结点的钱数
public:
int rob(TreeNode* root) {
if (root == NULL)return 0;
return DFS(root);
}
int DFS(TreeNode* root)
{
if (root == NULL)return 0;
// 抢,然后去下下家
int do_it = root->val
+ (root->left == NULL ?
0 : DFS(root->left->left) + DFS(root->left->right))
+ (root->right == NULL ?
0 : DFS(root->right->left) + DFS(root->right->right));
// 不抢,然后去下家
int not_do = DFS(root->left) + DFS(root->right);
int res = max(do_it, not_do);
return res;
}
};
发现思路1并不能AC,这是因为递归会导致重复搜索最终超时,这其实还是相当于递归,只是用到了动态规划的思想,因为没办法从最小的子问题来求解。
改进思路:记忆化搜索:创建map<TreeNode*,int>来存储子问题的解。
class Solution {
public:
map<TreeNode*, int> memo;
int rob(TreeNode* root) {
if (root == NULL)return 0;
return DFS(root);
}
int DFS(TreeNode* root)
{
if (root == NULL)return 0;
// 利用备忘录消除重叠子问题
if (memo.count(root))
return memo.find(root)->second;
// 抢,然后去下下家
int do_it = root->val
+ (root->left == NULL ?
0 : DFS(root->left->left) + DFS(root->left->right))
+ (root->right == NULL ?
0 : DFS(root->right->left) + DFS(root->right->right));
// 不抢,然后去下家
int not_do = DFS(root->left) + DFS(root->right);
int res = max(do_it, not_do);
memo.insert(make_pair(root, res));
return res;
}
};
这样就可以AC了!