1 198. 打家劫舍
当时自己写,AC代码:
class Solution {
public:
int dp[120]; // 从左往右偷 偷到第i个房子(不包含本房子)时候已经赚了的最多钱
/*
dp[i] = max(dp[i-1] + 0,dp[i-2]+nunms[i-2])
dp[0] = 0
dp[1] = 0
升序
模拟 样例2=== 0 0 2 7 11
*/
int rob(vector<int>& nums)
{
dp[0] = 0;
dp[1] = 0;
if(nums.size() == 1)return nums[0];
int i = 2;
for(; i < nums.size();i++)
dp[i] = max(dp[i-1] + 0,dp[i-2]+nums[i-2]);
return max(dp[i-1] + nums[i - 1],dp[i-2]+nums[i-2]); // 这里的return 和状态转移方程不太一样
}
};
2 213. 打家劫舍 II(打家劫舍的环形版)
一开始我的做法是在假设一个取不取,好像这样会决定隔壁的。但是题解换个角度看问题:
(1)假装没有首元素 考虑(考虑而不是决定)后面的元素来一遍普通版打家劫舍
(2)假装没有尾元素 考虑前面的元素来一遍普通版打家劫舍
学了题解的思想,AC代码:
class Solution {
public:
/*
问题1:哪个开始 随机or最大值
最大值是不是必取?不是 所以随机选一个开始 假定是1
*/
int dp[110]; // 偷到i房子时候最大的钱数
/*
我一开始的思路——
dp[j] = max(dp[j - 2] + nums[j],dp[j-1])
取 不取
nums.size()-1 取
dp[0] = 0; dp[1] = nums[1];
nums.size()-1 不取
dp[0] = nums[0]; dp[1] = dp[0];
分类i++
模拟——
*/
/*
上述我的假设 假定了一些格子取或者不取 然后就好像决定临近格子选择情况
这样会把自己绕进去,不好分类讨论
看了题解:
(1)假装没有首元素 考虑(考虑而不是决定)后面的元素来一遍普通版打家劫舍
(2)假装没有尾元素 考虑前面的元素来一遍普通版打家劫舍
dp[j] = max(dp[j - 2] + nums[j],dp[j-1])
头元素
dp[0] = nums[0]
头元素后一个元素
dp[1] = max(nums[0],nums[1])
*/
int rob(vector<int>& nums)
{
if(nums.size() == 1)return nums[0];
if(nums.size() == 2)return max(nums[0],nums[1]);
int ans1 = normalrob(nums,0,nums.size() - 2);
int ans2 = normalrob(nums,1,nums.size() - 1);
return max(ans1,ans2);
}
int normalrob(vector<int>& nums,int l,int r)
{
dp[l] = nums[l];
dp[l + 1] = max(nums[l],nums[l+1]);
for(int i = l + 2; i <= r; i++) dp[i] = 0;
for(int i = l + 2; i <= r; i++)
dp[i] = max(dp[i - 2] + nums[i],dp[i-1]);
return dp[r];
}
};
3 337. 打家劫舍 III(打家劫舍的树形版)
第一次做,难想到一个两个元素的数组构成的dp数组,看了题解详见注释,AC代码:
/**
* 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:
//int dp[10010]; // dp[i] 表示偷到节点i(考虑节点i)时候的最大钱数
/*
想了三十秒 想不出后看题解。
树形dp 可以不用一个死的dp数组 可以用动态的递归函数返回数组作为状态转移的容器
*/
// 函数返回值是dp数组 下标为0表示当前节点偷时候的最大钱数
// 下标为1表示当前节点不偷时候的最大钱数
vector<int> dfs(TreeNode* root)
{
if(root == nullptr)return {0,0};
vector<int> left = dfs(root->left);
vector<int> right = dfs(root->right);
// 偷当前节点
int val1 = root->val + left[1] + right[1];
// 不偷当前节点 那么左儿子偷和不偷都行 右儿子也是
int val2 = max(left[0],left[1]) + max(right[0],right[1]);
return {val1,val2};
}
int rob(TreeNode* root) {
auto t = dfs(root);
return max(t[0],t[1]);
}
};