【每日算法Day 106】打家劫舍系列最後一彈,撐住你就贏了!

題目鏈接

LeetCode 337. 打家劫舍 III[1]

往期回顧:打家劫舍 I :
【每日算法Day 104】偷電瓶的周某今天放出來了,還不趕緊做這道題防範一下![2]

往期回顧:打家劫舍 II :
【每日算法Day 105】打家劫舍第二彈:看好你的電瓶車![3]

題目描述

在上次打劫完一條街道之後和一圈房屋後,小偷又發現了一個新的可行竊的地區。這個地區只有一個入口,我們稱之爲。 除了之外,每棟房子有且只有一個房子與之相連。一番偵察之後,聰明的小偷意識到這個地方的所有房屋的排列類似於一棵二叉樹。 如果兩個直接相連的房子在同一天晚上被打劫,房屋將自動報警。

計算在不觸動警報的情況下,小偷一晚能夠盜取的最高金額。

示例1

        輸入:
[3,2,3,null,3,null,1]
     3
    / \
   2   3
    \   \ 
     3   1
輸出:
7
解釋:
小偷一晚能夠盜取的最高金額 = 3 + 3 + 1 = 7.
      

示例2

        輸入:
[3,4,5,1,3,null,1]
     3
    / \
   4   5
  / \   \ 
 1   3   1
輸出:
9
解釋:
小偷一晚能夠盜取的最高金額 = 4 + 5 = 9.
      

題解

這次是在一棵樹上偷竊了,做法還是一樣。對於結點 r 來說,我們還是分爲偷和不偷兩種情況。

如果偷的話,它的左右兒子就不能偷了,所以最大價值就是左兒子不偷的最大價值,加上右兒子不偷的最大價值,再加上 r 的價值。

而如果不偷的話,最大價值就是左兒子偷或不偷的最大價值,加上右兒子偷或不偷的最大價值。

因爲需要用到兒子結點偷和不偷兩個價值,所以需要在 dfs 時返回兩個值,用來表示偷和不偷兩個最大價值,具體實現時用 pair 來表示。

可能有人會用另一種實現方式,用 dfs0 表示不偷的最大價值,dfs1 表示偷的最大價值,然後兩個函數互相調用。注意這樣理論上是可行的,但是會產生很多重複計算,最終會超時。所以這種方法需要記憶化搜索,比較麻煩,需要用 map<TreeNode*, int> 來保存每個結點的答案。

代碼

複雜實現方式(c++)

        class Solution {
public:
    unordered_map<TreeNode*, int> dp0, dp1;

    int dfs0(TreeNode* root) {
        if (!root) return 0;
        if (dp0.find(root) != dp0.end()) return dp0[root];
        int res = 0;
        res += max(dfs0(root->left), dfs1(root->left));
        res += max(dfs0(root->right), dfs1(root->right));
        return dp0[root]=res;
    }

    int dfs1(TreeNode* root) {
        if (!root) return 0;
        if (dp1.find(root) != dp1.end()) return dp1[root];
        int res = root->val;
        res += dfs0(root->left);
        res += dfs0(root->right);
        return dp1[root]=res;
    }

    int rob(TreeNode* root) {
        if (!root) return 0;
        dp0.clear();
        dp1.clear();
        return max(dfs0(root), dfs1(root));
    }
};

      

精簡實現方式(c++)

        class Solution {
public:
    typedef pair<int, int> pii;

    pii dfs(TreeNode* root) {
        if (!root) return {0, 0};
        pii l = dfs(root->left);
        pii r = dfs(root->right);
        int r0 = max(l.first, l.second) + max(r.first, r.second);
        int r1 = l.first + r.first + root->val;
        return {r0, r1};
    }

    int rob(TreeNode* root) {
        pii res = dfs(root);
        return max(res.first, res.second);
    }
};

      

參考資料

[1]

LeetCode 337. 打家劫舍 III: leetcode-cn.com/problem

[2]

【每日算法Day 104】偷電瓶的周某今天放出來了,還不趕緊做這道題防範一下!: godweiyang.com/2020/04/

[3]

【每日算法Day 105】打家劫舍第二彈:看好你的電瓶車!: godweiyang.com/2020/04/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章