最小支配集
从图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;
}
}
}
};
由于调用的子问题过多,样例超时
解法二:每次返回三个状态减少调用次数
调用一次返回三个状态,可以减少调用子问题的次数
#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;
}
};