代码随想录Day41

今天学习贪心算法中最后的一道题。

968.监控二叉树

给定一个二叉树,我们在树的节点上安装摄像头。

节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。

计算监控树的所有节点所需的最小摄像头数量。

示例 1:

代码随想录Day41_第1张图片

 

输入:[0,0,null,0,0]
输出:1
解释:如图所示,一台摄像头足以监控所有节点。

思路:

1.本题一开始确实毫无思路,借助贪心的思路只能想到尽可能让摄像机的覆盖范围覆盖满,想到一旦放了一个摄像机就隔两格再放一个摄像机

2.顺着贪心的思路想,既然要让覆盖范围尽量覆盖满,那肯定叶子结点不放摄像机而是让叶子结点的父节点放摄像机,这样就能节省摄像机的数量。

3.至此至少理清楚了两点:(1)在叶子结点的父节点放摄像机;(2)一旦放了一个摄像机,之后每隔两个结点放一个摄像机。

而既然要考虑叶子结点不放摄像机而是让叶子结点的父节点放摄像机,想到二叉树的遍历顺序,决定采取后序遍历,这样才满足从底向上进行判断。

4.最后也是本题最难想到的一点:状态转移。状态转移的思想个人在这之前几乎没有接触过,唯一思想上可能有点类似的也就只有最近在unity开发上正在学习的有限状态机。所

有结点的状态无外乎也就只有三种情况:(1)没有被摄像机覆盖;(2)放了摄像机;(3)被摄像机覆盖了。没有放摄像机的状态被包括在了(1)和(3)中。

既然决定了是自底向上后序遍历,自然也要通过左右孩子的状态来决定当前结点的状态。那么左右节点的状态有多少种情况呢?乍一想好像有9种,但实际上很多情况可以用同一种处理方法。最终可以划分为三种情况:(1)左右孩子均为被覆盖,那么当前结点一定是无覆盖;(2)左右孩子至少有一方无覆盖,那么当前结点一定要放摄像机;(3)左右孩子至少有一方放了摄像机,那么当前结点一定是被覆盖。

(此处引用代码随想录中的讲解原图)

代码随想录Day41_第2张图片

 代码随想录Day41_第3张图片

 明确了以上的所有思路后,我们写出所有代码,但最后在通过示例上仍然有未通过部分,进一步模拟出错示例的遍历过程后发现了最后一种特殊情况:(4)遍历完毕后根节点仍未覆盖。因此我们在遍历完毕后还要判断根节点是否为无覆盖结点,如果是那么需要在根节点上再放一个摄像机。

class Solution {
public:
    int result;

    int traversal(TreeNode* cur){
        //遇到空节点,返回有覆盖状态
        if(cur == nullptr) return 2;

        int left = traversal(cur->left);//寻找左孩子的状态
        int right = traversal(cur->right);//寻找右孩子的状态
        //中,通过对左右孩子状态的情况进行处理
        //情况一:左右孩子均为有覆盖,此时当前结点一定是无覆盖状态
        if(left == 2 && right == 2){
            return 0;
        }
        //情况二:左右孩子至少有一个是无覆盖,那么当前结点一定要放摄像机
        if(left == 0 || right == 0){
            result++;
            return 1;
        }
        //情况三:左右孩子至少有一个是有摄像机,那么当前结点一定是有覆盖
        if(left == 1 || right == 1){
            return 2;
        }

        return -1;
    }

    int minCameraCover(TreeNode* root) {
        result = 0;
        //情况四:遍历结束后根节点依旧是无覆盖状态,此时需要在根节点上再放一个摄像头
        if(traversal(root) == 0){
            result++;
        }
        return result;
    }
};

启发:

1.对于最后的情况四,反思总结后发现实际上应当是贪心的结果:因为要尽可能使得摄像机的覆盖范围覆盖满,那么就尽量不会在叶子结点放摄像机,但如果在根节点放摄像机实际上也同样浪费了摄像机的覆盖范围。因此在遍历过程中不会在根节点考虑放摄像机,对于这一特殊情况得在遍历完成后对根节点独立进行判断。

2.对于状态转移这一思想,仍需要进一步理解。

3.对于遍历过程中遇到的空节点,到底应该取什么状态也有一定地讲究。对应到思路中我们所划分的几种状态,如果空节点为无覆盖状态,那么我们就得在叶子结点放摄像机了,这是我们应当避免的情况;如果空节点为有摄像机状态,那么我们叶子结点就是被覆盖状态了,此时为了让摄像机每隔两个结点防止一个,下一个摄像机应当是在叶子结点的爷爷结点,此时也不满足最后的结果(因为叶子结点实际上并没有被摄像机覆盖)。

你可能感兴趣的:(代码随想录,leetcode,算法,数据结构,c++,贪心算法)