栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

LeetCode

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

LeetCode

目录

一,题目描述

英文描述

中文描述

示例与说明

二,解题思路

算法

状态划分 

分情况讨论

三,AC代码

C++

Java

四,解题过程

第一博

第二搏

第三博


一,题目描述

英文描述

You are given the root of a binary tree. We install cameras on the tree nodes where each camera at a node can monitor its parent, itself, and its immediate children.

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

中文描述

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

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

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

示例与说明

提示:

  1. 给定树的节点数的范围是 [1, 1000]。
  2. 每个节点的值都是 0。

来源:力扣(LeetCode)
链接:力扣
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

二,解题思路

参考@笨猪爆破组【「手画图解」从递归优化到树形DP | 监控二叉树】,文章介绍的比较详细,感兴趣的可以戳链接。

算法

状态划分 

对于一个非空节点可以分为三种状态:

  1. 安装摄像头withCam(被自己监控);
  2. 不安装摄像头(被父节点监控watchByFat);
  3. 不安装摄像头(被子节点监控watchBySon);

若节点为空,需要注意:

  • 空节点不可能放置摄像头(此情况可以将返回值设置为INT_MAX,从而避免错误解被纳入结果考虑范围);
  • 空节点被父节点监控或是被子节点监控都返回0即可;

分情况讨论

1,当前节点安装摄像头:(子节点可装可不装,都会被当前节点监控)

  • minNum = min(minNum, left["withCam"] + right["watchByFat"]);
  • minNum = min(minNum, left["watchByFat"] + right["withCam"]);
  • minNum = min(minNum, left["watchByFat"] + right["watchByFat"]);
  • minNum = min(minNum, left["withCam"] + right["withCam"]);(此情况可以不用考虑,因为当前节点及左右子节点都安装摄像头的策略,基本不会是最优策略)

2,当前节点不安装摄像头:(子节点不可能被当前节点监控。只能被自己监控,或者被他们的子节点监控)

(当前节点被父节点监控。子节点可装可不装)

  • minNum = min(minNum, left["withCam"] + right["withCam"]);
  • minNum = min(minNum, left["withCam"] + right["watchBySon"]);
  • minNum = min(minNum, left["watchBySon"] + right["withCam"]);
  • minNum = min(minNum, left["watchBySon"] + right["watchBySon"]);

(当前节点被子节点监控。子节点至少有一个摄像头)

  • minNum = min(minNum, left["withCam"] + right["withCam"]);
  • minNum = min(minNum, left["withCam"] + right["watchBySon"]);
  • minNum = min(minNum, left["watchBySon"] + right["withCam"]);

至此便考虑到了所有可能成为解的情况,并将计算的结果一步步返回递归的上一层,从而重复利用。

三,AC代码

C++
class Solution {
public:
    int minCameraCover(TreeNode* root) {
        unordered_map ans = getMin(root);
        int minNum = 99999999;
        minNum = min(minNum, ans["withCam"]);
        // minNum = min(minNum, ans["watchByFat"]); // !!!根节点怎么可能被父节点监控
        minNum = min(minNum, ans["watchBySon"]);
        return minNum;
    }
    unordered_map getMin(TreeNode* root) {
        unordered_map ans;
        if (root == nullptr) {
            ans["withCam"] = 99999999;// 空节点不可能有摄像头,所以这里返回一个足够大的值,使得这种情况不可能被纳入最优解考虑范围
            ans["watchByFat"] = 0;// 空节点不需要被监控
            ans["watchBySon"] = 0;// 空节点不可能被子节点监控
            return ans;// !!!必须在这里返回
        }
        unordered_map left = getMin(root->left);
        unordered_map right = getMin(root->right);
        
        // 当前节点安装摄像头
        int minNum = 99999999;
        minNum = min(minNum, left["withCam"] + right["watchByFat"]);
        minNum = min(minNum, left["watchByFat"] + right["withCam"]);
        minNum = min(minNum, left["watchByFat"] + right["watchByFat"]);
        int withCam = 1 + minNum;

        // 当前节点不安装摄像头
        // 被父节点监控
        minNum = 99999999;
        minNum = min(minNum, left["withCam"] + right["withCam"]);
        minNum = min(minNum, left["withCam"] + right["watchBySon"]);
        minNum = min(minNum, left["watchBySon"] + right["withCam"]);
        minNum = min(minNum, left["watchBySon"] + right["watchBySon"]);
        int watchByFat = minNum;

        // 被子节点监控(子节点至少有一个摄像头)
        minNum = 99999999;
        minNum = min(minNum, left["withCam"] + right["withCam"]);
        minNum = min(minNum, left["withCam"] + right["watchBySon"]);
        minNum = min(minNum, left["watchBySon"] + right["withCam"]);
        int watchBySon = minNum;
        
        ans["withCam"] = withCam;
        ans["watchByFat"] = watchByFat;
        ans["watchBySon"] = watchBySon;
        return ans;
    }
    
};

Java
class Solution {
    public int minCameraCover(TreeNode root) {
        Reult ans = getMin(root);
        return Math.min(ans.withCam, ans.watchBySon);
    }
    public Reult getMin(TreeNode root) {
        Reult ans = new Reult();
        if (root == null) {
            ans.withCam = 99999999;
            ans.watchByFat = 0;
            ans.watchBySon = 0;
            return ans;
        }
        Reult left = getMin(root.left);
        Reult right = getMin(root.right);
        // 在该节点放置摄像头
        ans.withCam = 99999999;
        ans.withCam = Math.min(ans.withCam, left.withCam + right.watchByFat);
        ans.withCam = Math.min(ans.withCam, left.watchByFat + right.withCam);
        ans.withCam = Math.min(ans.withCam, left.watchByFat + right.watchByFat);
        ans.withCam++;

        // 该节点不放置摄像头(被父节点监控)
        ans.watchByFat = 99999999;
        ans.watchByFat = Math.min(ans.watchByFat, left.withCam + right.watchBySon);
        ans.watchByFat = Math.min(ans.watchByFat, left.watchBySon + right.withCam);
        ans.watchByFat = Math.min(ans.watchByFat, left.withCam + right.withCam);
        ans.watchByFat = Math.min(ans.watchByFat, left.watchBySon + right.watchBySon);

        // 该节点不放置摄像头(被子节点监控)
        ans.watchBySon = 99999999;
        ans.watchBySon = Math.min(ans.watchBySon, left.withCam + right.watchBySon);
        ans.watchBySon = Math.min(ans.watchBySon, left.watchBySon + right.withCam);
        ans.watchBySon = Math.min(ans.watchBySon, left.withCam + right.withCam);

        return ans;
    }
}
class Reult {
    int withCam;
    int watchByFat;
    int watchBySon;
}

 

四,解题过程

第一博

这种一般直接想到用递归,只要归纳全部的情况就能得到正确答案,虽然看上去效率很低,但是现实现出来再说

class Solution {
    public int minCameraCover(TreeNode root) {
        return Math.min(getAns(root, 0), getAns(root, 1));

    }
    // flag表示当前节点是否安装摄像头
    public int getAns(TreeNode root, int flag) {
        if (root == null) return 0;
        int minNum = 99999999;
        if (flag == 1) {
            minNum = Math.min(minNum, getAns(root.left, 1) + getAns(root.right, 1));
            minNum = Math.min(minNum, getAns(root.left, 1) + getAns(root.right, 0));
            minNum = Math.min(minNum, getAns(root.left, 0) + getAns(root.right, 1));
            minNum = Math.min(minNum, getAns(root.left, 0) + getAns(root.right, 0));
        } else {
            if (root.left != null && root.right != null) {
                minNum = Math.min(minNum, getAns(root.left, 1) + getAns(root.right, 1));
                minNum = Math.min(minNum, getAns(root.left, 1) + getAns(root.right, 0));
                minNum = Math.min(minNum, getAns(root.left, 0) + getAns(root.right, 1));
            } else if (root.left == null && root.right == null) {
                return flag;
            } else if (root.left == null) {
                minNum = Math.min(minNum, getAns(root.left, 0) + getAns(root.right, 1));
            } else if (root.right == null) {
                minNum = Math.min(minNum, getAns(root.left, 1) + getAns(root.right, 0));
            }
        }
        System.out.println(minNum + flag);
        return minNum + flag;
    }
}

显然上面的方法没有考虑到父节点的状态,因此没有包含所有的状态,最后只过了121/171

第二搏

参考了大佬的解题思路,发现是自己考虑的情况不够全面 ,比如只考虑当前节点放不放摄像头,未考虑父节点或子节点对当前节点监控状态的影响,这样就无法完全保证覆盖关系,无法判断连续两个节点不放置摄像头的情况。

因此需要引入当前节点是否被监视的参数。

class Solution {
    public int minCameraCover(TreeNode root) {
        return Math.min(getNum(root, true, true), getNum(root, false, false));
    }
    // hasCame: 该节点是否有摄像头,isWatched: 是否被自己或父节点监控
    public int getNum(TreeNode root,boolean hasCame, boolean isWatched) {
        if (root == null) {
            if (hasCame) return 99999999;
            else return 0;
        }
        int minNum = 99999999;
        // 该节点有摄像头(子结点可装可不装,舍弃子结点全部安装的情况,因为该情况不可能作为最终解)
        if (hasCame) {
            minNum = Math.min(minNum, getNum(root.left, true, true) + getNum(root.right, false, true) + 1);
            minNum = Math.min(minNum, getNum(root.left, false, true) + getNum(root.right, true, true) + 1);
            minNum = Math.min(minNum, getNum(root.left, false, true) + getNum(root.right, false, true) + 1);
        } 
        // 该节点无摄像头
        else {
            // 父节点有摄像头,被父节点监控(子结点可装可不装)
            if (isWatched) {
                minNum = Math.min(minNum, getNum(root.left, true, true) + getNum(root.right, true, true));
                minNum = Math.min(minNum, getNum(root.left, false, false) + getNum(root.right, true, true));
                minNum = Math.min(minNum, getNum(root.left, true, true) + getNum(root.right, false, false));
                minNum = Math.min(minNum, getNum(root.left, false, false) + getNum(root.right, false, false));
            } 
            // 父节点无摄像头,被子结点监控(至少有一个子结点要装摄像头)
            else {
                minNum = Math.min(minNum, getNum(root.left, true, true) + getNum(root.right, false, false));
                minNum = Math.min(minNum, getNum(root.left, false, false) + getNum(root.right, true, true));
                minNum = Math.min(minNum, getNum(root.left, true, true) + getNum(root.right, true, true));
            }
        }
        return minNum;
    }
}

显然,后面的测试用例都是超时了。

第三博

可以从上面的算法中看出,为保证正确性,多次对同一颗子树进行求解,导致计算重复,如果能够在一次递归中将各种情况的计算结果一次性返回,并且重复利用就可以提高效率。这便是树形DP

解题过程用的是map,这样看起来更直观一点,但是开销比较大,想提高效率可以用数组代替 

 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/356800.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号