题目来源:https://leetcode-cn.com/problems/binary-tree-cameras/
给定一个二叉树,我们在树的节点上安装摄像头。
节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。
计算监控树的所有节点所需的最小摄像头数量。
示例 1:
输入:[0,0,null,0,0]
输出:1
解释:如图所示,一台摄像头足以监控所有节点。
示例 2:
输入:[0,0,null,0,null,0,null,null,0]
输出:2
解释:需要至少两个摄像头来监视树的所有节点。 上图显示了摄像头放置的有效位置之一。
提示:
给定树的节点数的范围是 [1, 1000]。
每个节点的值都是 0。
题解:暴力超时:遍历各个节点,每个节点两种情况,亮灯或者不亮灯(代表相机),是否亮灯影响下面节点,所以加一个遍历参数间隔,间隔最大可以为2,当为2时必然要亮灯,当深度为1时两个孩子亮1个即可,取各情况的最小值即为最优解。但是节点数量最大为1000,暴力时间复杂度log(2*2)n,肯定超时,应该是动态规划解,后面补充正确解法。
1 public class 监控二叉树968 { 2 3 public int minCameraCover(TreeNode root, int interval) { 4 5 if(root == null){ 6 return 0; 7 } 8 if(interval == 1 && root.left == null && root.right == null){ 9 //当没有孩子节点,父节点也没有照到,则亮灯 10 root.val = 1; 11 return 1; 12 } 13 14 if(interval == 1 && root.left != null && root.right != null){ 15 // 当有1个间隔,两个孩子,两个孩子中的一个能照到此节点就好 16 root.val = 1; 17 int x = minCameraCover(root.left, 0) + minCameraCover(root.right, 0) + 1; 18 root.val = 0; 19 int y = minCameraCover(root.left, interval) + minCameraCover(root.right, interval + 1); 20 int z = minCameraCover(root.left, interval + 1) + minCameraCover(root.right, interval); 21 // 此节点可亮可不亮,三种情况对比,选择最优解 22 if(x < Math.min(y, z)){ 23 root.val = 1; 24 return x; 25 } else { 26 return Math.min(y, z); 27 } 28 } 29 30 if(interval == 2){ 31 //深度为2,一点要亮灯 32 root.val = 1; 33 return minCameraCover(root.left, 0) + minCameraCover(root.right, 0) + 1; 34 }else { 35 // 深度为1,此节点可亮可不亮 36 root.val = 1; 37 int x = minCameraCover(root.left, 0) + minCameraCover(root.right, 0) + 1; 38 root.val = 0; 39 int y = minCameraCover(root.left, interval + 1) + minCameraCover(root.right, interval + 1); 40 if(x < y){ 41 // 找到最小的情况 42 root.val = 1; 43 return x; 44 } else { 45 return y; 46 } 47 48 } 49 } 50 51 52 public int minCameraCover(TreeNode root){ 53 return minCameraCover(root, 1); 54 } 55 56 private static TreeNode buildTree(){ 57 int nextInt = Input.cin.nextInt(); 58 if(nextInt == 0){ 59 TreeNode treeNode = new TreeNode(0); 60 treeNode.left = buildTree(); 61 treeNode.right = buildTree(); 62 return treeNode; 63 } 64 return null; 65 } 66 public static void main(String[] args) { 67 TreeNode treeNode = buildTree(); 68 int x = new 监控二叉树968().minCameraCover(treeNode); 69 System.out.println(x); 70 } 71 }
正确解法:
前面的暴力明显反应了父亲,当前节点,和孩子的关系,那么可以将这些关系总结为3个状态,1. 当前节点有相机,2. 当前节点放不放都行(全局最优),3. 两个子树都被覆盖
然后遍历,在每个阶段都求最优解:
第1个状态:左右子树的第三个状态之和 + 当前节点的相机
第3个状态:左右子树的全局最优和
第2个状态: 左树有相机+右树最优 或 左树最优+右树有相机
得到最终代码:
1 public class 监控二叉树968 { 2 3 4 public int[] trave(TreeNode root){ 5 if(root == null){ 6 //当前root有相机, 当前root放或者不放摄像头都行,两个子树被覆盖(不管root) 7 return new int[]{Integer.MAX_VALUE / 2, 0, 0}; 8 } 9 int[] left = trave(root.left); 10 int[] right = trave(root.right); 11 int[] array = new int[3]; 12 //两个孩子都被他们的孩子监控的最优解 + 1(有相机) 13 array[0] = left[2] + right[2] + 1; 14 // 左孩子有相机 或者右孩子有相机时分别加上另外一个孩子的最优解 15 array[1] = Math.min(array[0], Math.min(left[0] + right[1], left[1] + right[0])); 16 //不管当前节点,取孩子们的最优解 17 array[2] = Math.min(array[0], left[1] + right[1]); 18 return array; 19 } 20 public int minCameraCover(TreeNode root){ 21 int[] trave = trave(root); 22 return trave[1]; 23 } 24 25 private static TreeNode buildTree(){ 26 int nextInt = Input.cin.nextInt(); 27 if(nextInt == 0){ 28 TreeNode treeNode = new TreeNode(0); 29 treeNode.left = buildTree(); 30 treeNode.right = buildTree(); 31 return treeNode; 32 } 33 return null; 34 } 35 public static void main(String[] args) { 36 TreeNode treeNode = buildTree(); 37 int x = new 监控二叉树968().minCameraCover(treeNode); 38 System.out.println(x); 39 } 40 }