Leetcode 968. Binary Tree Cameras

原題

Given a binary tree, we install cameras on the nodes of the tree.

Each camera at a node can monitor its parent, itself, and its immediate children.

Calculate the minimum number of cameras needed to monitor all nodes of the tree.

Example 1:

在這裏插入圖片描述

Input: [0,0,null,0,0]
Output: 1
Explanation: One camera is enough to monitor all nodes if placed as shown.

Example 2:

在這裏插入圖片描述

Input: [0,0,null,0,null,0,null,null,0]
Output: 2
Explanation: At least two cameras are needed to monitor all nodes of the tree. The above image shows one of the valid configurations of camera placement.

題目大意:

在二叉樹節點放置攝像頭,每個攝像頭覆蓋到父節點、本身、以及子節點,最後求得覆蓋所有節點所需最小攝像頭數量。

分析

Ref:https://leetcode.com/articles/binary-tree-cameras/

思路比較明確,使用動態規劃來求解全局最優解。這裏最主要是到對每個節點的狀態劃分。

由於是樹形結構,按照自下到上的思路來對當前節點(cur_node)的狀態進行劃分,節點總共有三種狀態:

狀態0:
在這裏插入圖片描述
兩個子節點均被覆蓋,當前節點不被覆蓋。
狀態1:
在這裏插入圖片描述
當前節點被覆蓋,但是當前節點沒有放置相機(說明兩個子節點中至少有一個放置了相機)
狀態2:
在這裏插入圖片描述
當前節點放置了相機

總結

對當前的節點進行劃分之後,可以得到以下的狀態轉移條件

  1. 爲了滿足狀態0,則cur_node的兩個子節點都必須處於狀態1
  2. 爲了滿足狀態1,則cur_node的兩個子節點中至少有一個子節點處於狀態2,另一個子節點處於狀態1或狀態2
  3. 對於狀態2,由於cur_node放置相機,兩個子節點可以處於任何狀態

遞歸函數定義

令solve(node)函數爲求解函數,返回當node狀態分別爲0,1,2時,滿足其子樹全部被相機覆蓋的最優解(dp[0], dp[1], dp[2])
即:

  • 當node爲狀態0時,返回覆蓋其子樹所有節點所需的最小相機數(dp[0])
  • 當node爲狀態1時,返回覆蓋其子樹所有節點所需的最小相機數(dp[1])
  • 當node爲狀態2時,返回覆蓋其子樹所有節點所需的最小相機數(dp[2])

Note: 由於狀態0沒有覆蓋到當前節點,因此要求解覆蓋包含當前節點的最優解時,其解爲min(dp[1], dp[2])。

遞歸出口
噹噹前節點爲NULL時,跳出遞歸,返回解爲(0, 0, MAX_VALUE),這裏假設子節點爲空時,可以視爲被相機覆蓋,MAX_VALUE表示當前節點不可能是2狀態
在這裏插入圖片描述

如上圖爲例,如下對只有1個或2個節點的情況進行解釋:

噹噹前節點爲NULL時,我們關注父節點(2)的狀態,(這裏假設子節點爲空時,可以視爲被相機覆蓋)

  1. 因此節點2處於狀態0時,得到的最優解爲0,因爲子節點均被覆蓋,不需要額外相機
  2. 因此節點2處於狀態1時,其不可能是由子節點的相機覆蓋得到的,因爲子節點沒有放置相機,其值爲MAX_VALUE
  3. 因此節點2處於狀態2時,放置一個相機,最優解爲1

只有一個節點2時,得到的節點2不同狀態的最優解爲(0, MAX_VALUE, 1),最優解爲min(MAX_VALUE, 1) = 1.

再往回推當節點2有一個父節點1時,將節點1最爲當前節點。

  1. 由於節點2不可能由其子節點覆蓋得到,因此此狀態下的最優解爲MAX_VALUE,表示不可能
  2. 當節點1爲狀態1時,由子節點覆蓋得到,此狀態下最優解1
  3. 當節點1爲狀態2時,在此位置上放置相機,子節點可以爲任何狀態,此狀態下最優解1

只有兩個節點時,得到的節點1(root)不同狀態的最優解爲( MAX_VALUE, 1, 1),最優解爲min(1, 1) = 1

他山之石,可以攻玉(別人的答案)

在提交之後可以看到別人提交的答案,看完後再看看自己的代碼便更覺得羞澀難擋,因此去掉了貼自己代碼這一步,轉而分析別人代碼。

作者@awice

第一次看到代碼的時候反覆看了幾次纔看懂,爲了便於大家理解,以註釋的形式做個筆記

class Solution {
public:
    int minCameraCover(TreeNode* root)
    {
        vector<int> ans = solve(root);
		//由於狀態0不滿足覆蓋條件,因此噹噹前節點爲root時, 選擇滿足狀態1和狀態2中的最小值
        return min(ans[1], ans[2]);

    }
    vector<int> solve(TreeNode* node) {
		//按照之前遞歸出口的分析,這裏將MAX_VALUE設爲了99999
        if(node == NULL)
            return vector<int>{0, 0, 99999};

        //對當前節點的左子節點以及右子節點計算最優解值
        vector<int> L = solve(node->left);
        vector<int> R = solve(node->right);
		//求解左子節點以及右子節點中滿足的狀態1、狀態2的最小值
		//也就是全部覆蓋左子樹、右子樹所需相機數的最優解
        int mL12 = min(L[1], L[2]);
        int mR12 = min(R[1], R[2]);
        //噹噹前節點爲狀態0時,按照【總結1】,兩個子節點必須處於狀態1.
        //因此得到的最優解爲L[1]+R[1]
        int d0 = L[1] + R[1];
        //噹噹前節點爲狀態1時,按照【總結2】,兩個子節點中至少有一個節點處於狀態2,另一個子節點處於狀態1或2
        //因此得到的最優解爲這兩種情況的最小值
        int d1 = min(L[2] + mR12, R[2] + mL12);
        //噹噹前節點爲狀態2時,按照【總結3】, 只要求得兩個子節點的最優解在加上當前節點放置的相機數1即可
        int d2 = 1 + min(L[0], mL12) + min(R[0], mR12);

        return vector<int>{d0, d1, d2};
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章