打家劫舍问题是经典的动态问题
198. 打家劫舍
第一道比较简单,dp[i]表示偷到第i家的时候最大收益,有两种选择,一种是偷第i家一种是不偷,偷的话收益为dp[i - 2] + value[i],即偷第i - 2家的最大收益加上第i家的收益,不偷的收益为dp[i - 1],这个好理解吧。
所以递推公式为:
dp[i] = max(dp[i - 2] + value[i], dp[i - 1])
从递推公式也可以看出了要初始化dp[0]和dp[1]。最终代码为:
class Solution {
public:
int rob(vector<int>& nums) {
int n = nums.size();
if (n == 1)
return nums[0];
vector<int> dp(n, 0);
dp[0] = nums[0];
dp[1] = max(dp[0], nums[1]);
for (int i = 2; i < n; i++) {
dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);
}
return dp[n - 1];
}
};
还有一个更简易的版本,即只考虑每家偷不偷,偷的收益记录下来,不偷的收益记录下来,代码为:
class Solution {
public:
int rob(vector<int>& nums) {
int dp[2];
dp[0] = 0;
dp[1] = 0;
int n = nums.size();
for (int i = 0; i < nums.size(); i++) {
int temp = dp[0];
dp[0] = max(dp[0], dp[1]);
dp[1] = temp + nums[i];
}
return dp[0] > dp[1] ? dp[0] : dp[1];
}
};
这种写法空间复杂度降为O(1)。
213. 打家劫舍 II
这道题如果会做上面那道题也比较简单,环状影响的主要就是第一个和最后一个,对中间的递推方程没有影响,当推导最后一个时,要考虑第一个有没有偷,如果没偷那最后一个正常递归,如果偷了那最后一个就不能再偷了,所以分两种情况考虑即可,即分为偷第一个和不偷第一个,然后比较两种情况哪种情况收益大就行了。
class Solution {
public:
int rob(vector<int>& nums) {
int n = nums.size();
if (n == 1)
return nums[0];
vector<int> dp(n, 0);
dp[1] = nums[1];
int sum1 = 0;
for (int i = 2; i < n; i++) {
dp[i] = max(dp[i - 1], dp[i - 2] + nums[i]);
}
sum1 = dp[n - 1];
dp[0] = nums[0];
dp[1] = dp[0];
int sum2 = 0;
for (int i = 2; i < n - 1; i++) {
dp[i] = max(dp[i - 1], dp[i - 2] + nums[i]);
}
sum2 = dp[n - 2];
return max(sum1, sum2);
}
};
337. 打家劫舍 III
这个题看起来有点难度,其实和前两道题差不多,只是在树上进行动态规划而已,每一个节点有两种情况,一种是不偷,即直接取左右子节点金额的和,一种是偷,这个时候就要取孙子节点的和了,所以递归的时候要传递两个参数,一个是左右子节点的和,一个是本身该节点的最大收益,传给父节点,父节点再利用左右子树共四个参数来求该节点的最大收益,然后再传递相应参数。
/**
* 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> traversal(TreeNode* node) {
vector<int> temp(2, 0);
if (!node->left && !node->right) {
temp[1] = node->val;
}
else {
vector<int> left_num(2, 0);
vector<int> right_num(2, 0);
if (node->left)
left_num = traversal(node->left);
if (node->right)
right_num = traversal(node->right);
temp[0] = left_num[1] + right_num[1];
temp[1] = max(left_num[1] + right_num[1], node->val + left_num[0] + right_num[0]);
}
return temp;
}
int rob(TreeNode* root) {
vector<int> result = traversal(root);
return result[1];
}
};