最小支配集
从图G=(V,E)中,从点集V中选取一个子集P,使得图中其他任意一个节点可以与P中的点相连
照相机的集合可以看成集合P
每一个节点:
有两个变量需要考虑
是否放置了照相机
是否被监控
解法1:
从上至下求解 对于每一个子树 根节电的情况是已知的
 1.放置了照相机被监控
 2.没有放置照相机,被监控
 3.没有放置照相机,并且没有被监控
 isPlaced 表示是否放置了照相机
 isCoverd 表示该节点是否被覆盖
class Solution {
public:
    int minCameraCover(TreeNode* root) {
        return min(findans(root,1,1),findans(root,0,0));
    }
    int findans(TreeNode* root,bool isPlaced,bool isCoverd){
        
        if(root==nullptr){
            return 0;
        }
        else{// have two children
            if(isPlaced){
                int ret = INT_MAX;
                int lp = findans(root->left,1,1);
                int lnp = findans(root->left,0,1);
                int rp = findans(root->right,1,1);
                int rnp = findans(root->right,0,1);
                ret = min(ret,lnp + rnp);
                ret = min(ret,lp + rp);
                ret = min(ret,lnp + rp);
                ret = min(ret,lp + rnp);
                return ret+1;
            }else{
                int ret = INT_MAX;
                if(isCoverd){
                    if(root->left==nullptr && root->right==nullptr){
                        return 0;
                    }else if(root->left!=nullptr && root->right!=nullptr){
                        int lp = findans(root->left,1,1);
                        int lnp = findans(root->left,0,0);
                        int rp = findans(root->right,1,1);
                        int rnp = findans(root->right,0,0);
                        ret = min(ret,lp + rp);
                        ret = min(ret,lnp + rp);
                        ret = min(ret,lp + rnp);
                        ret = min(ret,lnp + rnp);
                    }else if(root->left!=nullptr){
                        ret = min(ret,findans(root->left,1,1));
                        ret = min(ret,findans(root->left,0,0));
                    }else if(root->right!=nullptr){
                        ret = min(ret,findans(root->right,1,1) );
                        ret = min(ret,findans(root->right,0,0) );
                    }
                }else{
                    if(root->left==nullptr && root->right==nullptr){
                        return 1;
                    }else if(root->left!=nullptr && root->right!=nullptr){
                        int lp = findans(root->left,1,1);
                        int lnp = findans(root->left,0,0);
                        int rp = findans(root->right,1,1);
                        int rnp = findans(root->right,0,0);
                        ret = min(ret,lp+ rp);
                        ret = min(ret,lnp + rp);
                        ret = min(ret,lp + rnp);
                    }else if(root->left!=nullptr){
                        ret = min(ret,findans(root->left,1,1));
                    }else if(root->right!=nullptr){
                        ret = min(ret,findans(root->right,1,1) );
                    }
                }
                return ret;
            }
        }
    }
};
由于调用的子问题过多,样例超时
解法二:每次返回三个状态减少调用次数
参考
https://leetcode-cn.com/problems/binary-tree-cameras/solution/shou-hua-tu-jie-cong-di-gui-you-hua-dao-dong-tai-g/
调用一次返回三个状态,可以减少调用子问题的次数
#define min3(a,b,c) min(min(a,b),c);
#define min4(a,b,c,d) min(min(min(a,b),c),d);
class Solution {
public:
int minArr(vector<int>arr){
return min(min(arr[0],arr[1]),arr[2]);
}
int minCameraCover(TreeNode*root) {
if (root == nullptr) return 0;
vector<int> ret = findans(root);
return min(ret[0],ret[2]);
}
vector<int> findans(TreeNode * root){
// 0 not place cam not cover by father
// 1 not place cam cover by father
// 2 place cam cover by self
if(root==nullptr){
return {0,0,1000000};
}
vector<int> ret(3,0);
vector<int> left = findans(root->left);
vector<int> right = findans(root->right);
ret[0] = min3(left[0] + right[2],left[2] + right[0],left[2] + right[2]);
ret[1] = min4(left[0] + right[2],left[2] + right[0],left[2] + right[2],left[0] + right[0]);
ret[2] = 1 + min4(left[1] + right[1],left[2] + right[1],left[1] + right[2],left[2] + right[2]);
return ret;
}
};

解法三:贪心构造
可以简单证明
 这个问题的解是不唯一的
 只要可以构造出来一种解即可
 例如一个树只有两个节点,那么这两个位置放哪一个都可以
 解法一超时间因为调用子问题过多
 解法二虽然不超时,当时还是不够快,因为考虑了很多情况
 构造:
 我们使用贪心构造的办法,这个正确性需要证明,所以使用的时候需要谨慎
 从下至上进行构造,叶子节点一定不要放置
 两个子节点有一个放置了相机,那么父节点就不放置
 两个子节点如果有一个没被监控,那么父节点需要放置
 两个子节点如果都被监控了,那么父节点不放置相机
class Solution {
    public:
    int ans = 0;
    int minCameraCover(TreeNode*root) {
        if (root == nullptr) return 0;
        int ret = findans(root);
        if(ret==0){
            ans++;
        }
        return ans;
    }
    //返回 0 root 没有放置cam 并且没有被监控
    //返回 1 root 没有放置cam 并且被监控
    //返回 2 root 放置cam 并且被监控
    int findans(TreeNode*root){
        if(root==nullptr){
            return 1;
        }
        int left = findans(root->left);
        int right = findans(root->right);
        if( left==0 || right==0 ){
            ans ++;
            return 2;
        }
        if( left==2 || right==2 ){
            return 1;
        }
        return 0;
    }
};
                










