198. 打家劫舍
题目
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。
示例
输入: [1,2,3,1]
输出: 4
解释: 偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
代码
// 方法一
class Solution {
public:
int rob(vector<int>& nums) {
int len = nums.size();
if(len == 0) return 0;
vector<vector<int>> dp(len+1, vector<int>(2, 0));
dp[0][0] = 0, dp[0][1] = nums[0];
for(int i = 1; i < len; i++){
dp[i][0] = max(dp[i-1][0],dp[i-1][1]);
dp[i][1] = dp[i-1][0] + nums[i];
}
return max(dp[len-1][0], dp[len-1][1]);
}
};
// 方法二
class Solution {
public:
int rob(vector<int>& nums) {
int len = nums.size();
if(len == 0) return 0;
if(len == 1) return nums[0];
if(len == 2) return max(nums[0], nums[1]);
vector<int>dp(nums.size() + 3);
// dp[i] 表示从i位置开始最多可以抢到的钱
dp[len-1] = nums[len-1];
dp[len-2] = max(nums[len-1], nums[len-2]);
for(int i = len-3; i >=0; i--)dp[i] = max(nums[i] + dp[i+2], dp[i+1]);
return dp[0];
}
};
//方法三 优化空间
class Solution {
public:
int rob(vector<int>& nums) {
int len = nums.size();
if(len == 0) return 0;
if(len == 1) return nums[0];
if(len == 2) return max(nums[0], nums[1]);
int dp_i = 0, dp_i_1 = 0, dp_i_2 = 0;
for(int i = len-1; i >= 0; i--){
dp_i = max(dp_i_1, nums[i]+dp_i_2);
dp_i_2 = dp_i_1;
dp_i_1 = dp_i;
}
return dp_i;
}
};
213.打家劫舍II
题目
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都围成一圈,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。
示例
输入: [2,3,2]
输出: 3
解释: 你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
代码
class Solution {
public:
int rob(vector<int>& nums) {
int len = nums.size();
if(len == 1) return nums[0];
return max(robDp(nums, 0, len-2), robDp(nums, 1, len-1));
}
int robDp(vector<int> nums, int start, int end){
int dp_i = 0, dp_i_1 = 0, dp_i_2 =0;
for(int i = end; i >=start; i--){
dp_i = max(nums[i]+dp_i_2, dp_i_1);
dp_i_2 = dp_i_1;
dp_i_1 = dp_i;
}
return dp_i;
}
};
337. 打家劫舍 III
题目
在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。
计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。
示例
输入: [3,2,3,null,3,null,1]
3
/ \
2 3
\ \
3 1
输出: 7
解释: 小偷一晚能够盗取的最高金额 = 3 + 3 + 1 = 7.
代码
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int rob(TreeNode* root) {
if(root == NULL) return 0;
if(mp.find(root) != mp.end()) return mp[root];
int do_it = root->val + (root->left== NULL?0:(rob(root->left->left) + rob(root->left->right)))+(root->right == NULL ? 0 : (rob(root->right->left)+rob(root->right->right)));
int not_do = rob(root->left) + rob(root->right);
mp[root] = max(do_it, not_do);
return mp[root];
}
private:
unordered_map<TreeNode*, int> mp;
};
740. 删除与获得
题目
给定一个整数数组 nums ,你可以对它进行一些操作。
每次操作中,选择任意一个 nums[i] ,删除它并获得 nums[i] 的点数。之后,你必须删除每个等于 nums[i] - 1 或 nums[i] + 1 的元素。
开始你拥有 0 个点数。返回你能通过这些操作获得的最大点数。
示例
输入: nums = [3, 4, 2]
输出: 6
解释:
删除 4 来获得 4 个点数,因此 3 也被删除。
之后,删除 2 来获得 2 个点数。总共获得 6 个点数。
代码
// 方法一,转化为打家劫舍问题
class Solution {
public:
int deleteAndEarn(vector<int>& nums) {
// DP[i]表示i位置结尾的数组目前的最大点数
int len = nums.size();
if(len == 0) return 0;
if(len == 1) return nums[0];
vector<int> dp(len + 2);
int maxNum = 0;
for(int i = 0; i < len; i++) maxNum = max(maxNum, nums[i]);
vector<int> item(maxNum + 1, 0);
// 类似桶排序,找一个数组,记录所有数字出现次数。
// 如nums = [3, 2, 4] item = [0, 1,1,1]
for(int c : nums){
item[c]++;
}
// dp数组遍历的是从数字1 到maxNUM ,走到数字可以获得最大点数
dp[1] = item[1] * 1;
dp[2] = max(item[2] * 2, dp[1]);
for(int i = 3; i <=maxNum; i++ )dp[i] = max(dp[i-2] + item[i]*i, dp[i-1]);
return dp[maxNum];
}
};