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];
}
};