程式碼隨想錄演算法訓練營第四十七天| ● 198.打家劫舍 ● 213.打家劫舍II ● 337.打家劫舍III

SandaiYoung發表於2024-03-15

打家劫舍

題目連結:198. 打家劫舍 - 力扣(LeetCode)

思路:每一家的最大收益來源只有兩個,一個是這家不偷,那麼最大收益等於從上一家出來的最大收益,另一個是偷這一個家,因此最大收益等於從上上一家出來的最大收益加這一家的收益。

class Solution {
public:
    int rob(vector<int>& nums) {
        vector<int>dp(nums.size()+1,0);
        dp[0]=0;
        dp[1]=nums[0];
        for(int i=2;i<=nums.size();i++){
            dp[i]=max(dp[i-1],dp[i-2]+nums[i-1]);
        }
        return dp.back();
    }
};

打家劫舍II

題目連結:213. 打家劫舍 II - 力扣(LeetCode)

思路:本題和上一題的區別在於本題陣列是環形陣列,似乎無從下手。這裡參考官網寫法

程式碼隨想錄演算法訓練營第四十七天| ● 198.打家劫舍  ● 213.打家劫舍II   ● 337.打家劫舍III

class Solution {
public:
    int rob(vector<int>& nums) {
        if(nums.size()==0)return 0;
        if(nums.size()==1)return nums[0];
        int result1=R(nums,0,nums.size()-2);
        int result2=R(nums,1,nums.size()-1);
        return max(result1,result2);
    }
    int R(vector<int>& nums,int start,int end){
        if(start==end)return nums[start];
        vector<int> dp(nums.size(),0);
        dp[start]=nums[start];
        dp[start+1]=max(nums[start],nums[start+1]);
        for(int i=start+2;i<=end;i++){
            dp[i]=max(dp[i-1],dp[i-2]+nums[i]);
        }
        return dp[end];
    }
};

打家劫舍III

題目連結:337. 打家劫舍 III - 力扣(LeetCode)

思路:這一題讓我想到了放攝像頭那一題,總之不會。

遞迴法,也是我一開始想到的做法:

class Solution {
public:
    unordered_map<TreeNode* , int> umap; // 記錄計算過的結果
    int rob(TreeNode* root) {
        if (root == NULL) return 0;
        if (root->left == NULL && root->right == NULL) return root->val;
        if (umap[root]) return umap[root]; // 如果umap裡已經有記錄則直接返回
        // 偷父節點
        int val1 = root->val;
        if (root->left) val1 += rob(root->left->left) + rob(root->left->right); // 跳過root->left
        if (root->right) val1 += rob(root->right->left) + rob(root->right->right); // 跳過root->right
        // 不偷父節點
        int val2 = rob(root->left) + rob(root->right); // 考慮root的左右孩子
        umap[root] = max(val1, val2); // umap記錄一下結果
        return max(val1, val2);
    }
};

動態規劃法:

對於樹形dp,dp陣列的長度是2,因為我們有遞迴的過程,每個dp陣列只需儲存偷或不偷的最大收益。

class Solution {
public:
    int rob(TreeNode* root) {
        vector<int> result = robTree(root);
        return max(result[0], result[1]);
    }
    // 長度為2的陣列,0:不偷,1:偷
    vector<int> robTree(TreeNode* cur) {
        if (cur == NULL) return vector<int>{0, 0};
        vector<int> left = robTree(cur->left);
        vector<int> right = robTree(cur->right);
        // 偷cur,那麼就不能偷左右節點。
        int val1 = cur->val + left[0] + right[0];
        // 不偷cur,那麼可以偷也可以不偷左右節點,則取較大的情況
        int val2 = max(left[0], left[1]) + max(right[0], right[1]);
        return {val2, val1};
    }
};

相關文章