leetcode:968. 监控二叉树

题目来源

题目描述

在这里插入图片描述
在这里插入图片描述

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode() : val(0), left(nullptr), right(nullptr) {}
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
    TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};


class Solution {
public:
    int minCameraCover(TreeNode* root) {

    }
};

题目解析

难点在于:放相机会影响父节点

思路一

二叉树的问题,我们一般都是从一个点的角度看,思考解决问题时,有几种可能性?需要什么信息?

对于一个节点,它有什么状态,仅仅是放与不放相机吗?还有是否被监控到。

以x为的树,返回值有三种情况:

  • 1)X节点没有放相机,X是被监控的(被孩子监控),而且X下面所有点都被覆盖了。此时,最少需要几个相机
  • 2)X节点放了相机,X是被监控的(被自己监控),而且X下面所有点都被覆盖了。此时,最少需要几个相机
  • 3)X节点没有放相机,X没有被监控(可以由它的父节点补救),X下面所有的点都被监控了。此时,最少需要几个相机

为什么要求X下发的点都被覆盖?很明显,如果X没有被覆盖,至少有它的父节点能够补救它,但是X下面的点如果没有被覆盖,向上返回爷爷辈的节点就再也无法补救了

在这里插入图片描述

class Solution {
    // 潜台词:x是头节点,x下方的点都被覆盖的情况下
    struct Info{
        long uncovered;       // X节点没有放相机,X没有被监控(可以由它的父节点补救)。此时,至少需要几个相机
        long coveredNoCamera; // X节点没有放相机,X是被监控的(被孩子监控)。此时,x为头的树至少需要几个相机
        long coveredHasCamera; //X节点放了相机,X是被监控的(被自己监控)。x为头的树至少需要几个相机

        Info(long uncovered, long no, long has): uncovered(uncovered) {
            coveredNoCamera = no;
            coveredHasCamera = has;
        }
    };
    
    Info *process(TreeNode* root){
        if(root == nullptr){
            return new Info(INT32_MAX, 0, INT32_MAX);
        }
        
        auto left = process(root->left);
        auto right = process(root->right);

        // uncovered: X没有被监控-->左右孩子都不得放相机但是它们必须被覆盖的
        long uncovered = left->coveredNoCamera + right->coveredHasCamera;

        //X节点没有放相机,X是被监控的(被孩子监控)
        long coveredNoCamera = std::min(
                         left->coveredHasCamera + right->coveredHasCamera,
                std::min(left->coveredHasCamera + right->coveredNoCamera,
                         left->coveredNoCamera + right->coveredHasCamera)
                );
        //X节点放了相机,X是被监控的(被自己监控)
        long coveredHasCamera = 
                      std::min(left->uncovered, std::min(left->coveredNoCamera, left->coveredHasCamera))
                    + std::min(right->uncovered, std::min(right->coveredNoCamera, right->coveredHasCamera))
                    + 1;
        return new Info(uncovered, coveredNoCamera, coveredHasCamera);
     }
public:
    int minCameraCover(TreeNode* root) {
        auto info = process(root);
        return std::min(std::min( info->coveredHasCamera, info->coveredNoCamera), info->uncovered + 1  ); 
    }
};

贪心优化

(1)确定遍历方式

  • 在安排选择摄像头的位置的时候,我们要从底向上进行推导,因为尽量让叶子节点的父节点安装摄像头,这样摄像头的数量才是最少的,这也是本道贪心的原理所在!
  • 因此,才有后序遍历的方式

(2)确定状态转移
以X为头,X下面的节点都被监控,X节点有几种状态呢?

  • 当前节点有相机
  • 当前节点不需要相机(子节点有相机把它给覆盖了)
  • 当前节点没有相机并且也没有被子节点给覆盖(那么他只能等他的父节点把它给覆盖了)

(3)实现

class Solution {
    enum STATUS{
        UNCOVERED,
        COVERED_NO_CAMERA,
        COVERED_HAS_CAMERA
    } ;

    // 以x为头,x下方的节点都是被covered,得到的最优解中:
    // x是什么状态,在这种状态下,需要至少几个相机
    struct Info{
        STATUS status;
        int cameras;

        Info(STATUS status, int cameras) : status(status) , cameras(cameras){

        }
    };

    Info * process(TreeNode *root){
        if(root == nullptr){
            return new Info(COVERED_NO_CAMERA, 0);
        }

        auto left = process(root->left);
        auto right = process(root->right);

        int cameras = left->cameras + right->cameras;

        // 当前节点需要相机:左、或右,哪怕有一个没覆盖
        if(left->status == UNCOVERED || right->status == UNCOVERED){
            return new Info(COVERED_HAS_CAMERA, cameras + 1);
        }

        // 当前节点不需要相机
        if(left->status == COVERED_HAS_CAMERA || right->status == COVERED_HAS_CAMERA){
            return new Info(COVERED_NO_CAMERA, cameras);
        }


        return new Info(UNCOVERED, cameras);
    }
public:
    int minCameraCover(TreeNode* root) {
        auto info = process(root);
        return info->cameras + (info->status == UNCOVERED ? 1 : 0);
    }
};

思路三

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值