题目
给定一个二叉树,我们在树的节点上安装摄像头。
节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。
计算监控树的所有节点所需的最小摄像头数量。
示例 1:
输入:[0,0,null,0,0] 输出:1 解释:如图所示,一台摄像头足以监控所有节点。
示例 2:
输入:[0,0,null,0,null,0,null,null,0] 输出:2 解释:需要至少两个摄像头来监视树的所有节点。 上图显示了摄像头放置的有效位置之一。
解题
方法一:贪心
这是很重要的一个线索,摄像头可以覆盖上中下三层,如果把摄像头放在叶子节点上,就浪费的一层的覆盖。
所以把摄像头放在叶子节点的父节点位置,才能充分利用摄像头的覆盖面积。
那么有同学可能问了,为什么不从头结点开始看起呢,为啥要从叶子节点看呢?
因为头结点放不放摄像头也就省下一个摄像头, 叶子节点放不放摄像头省下了的摄像头数量是指数阶别的。
所以我们要从下往上看,局部最优:让叶子节点的父节点安摄像头,所用摄像头最少,整体最优:全部摄像头数量所用最少!
使用后续遍历,这样可以根据左右节点推断出该节点。
0代表该节点 无摄像头、无覆盖
1代表该节点 有摄像头、有覆盖
2代表该节点 无摄像头、有覆盖
class Solution { public: int res; 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){ //如果左右节点有一个无覆盖,就要让该节点放摄像头 res++; return 1; } if(left==1||right==1) return 2; //如果有有一个有摄像头,那么该节点表示覆盖 return -1;//不会到达这一步 } int minCameraCover(TreeNode* root) { res=0; if(traversal(root)==0) res++; return res; } };
精简一下
// 版本二 class Solution { private: int result; int traversal(TreeNode* cur) { if (cur == NULL) return 2; int left = traversal(cur->left); // 左 int right = traversal(cur->right); // 右 if (left == 2 && right == 2) return 0; else if (left == 0 || right == 0) { result++; return 1; } else return 2; } public: int minCameraCover(TreeNode* root) { result = 0; if (traversal(root) == 0) { // root 无覆盖 result++; } return result; } };
更加规范的写法
enum STAT{ Camera, Cover, NoCover }; class Solution { public: int res; STAT dfs(TreeNode* cur){ if(cur==nullptr) return Cover; STAT left=dfs(cur->left); STAT right=dfs(cur->right); if(left==Cover&&right==Cover) return NoCover; else if(left==NoCover||right==NoCover){ res++; return Camera; } else return Cover; } int minCameraCover(TreeNode* root) { res=0; if(dfs(root)==NoCover) res++; return res; } };