动态规划:监控二叉树

这篇博客探讨了一道关于在二叉树上用最少摄像头覆盖所有节点的问题。提供了两种解法:暴力搜索和动态规划。暴力搜索通过递归搜索所有可能的摄像头放置方案,而动态规划则使用状态表示节点是否被覆盖,通过DFS遍历优化解决方案。两种方法都旨在找到最小的摄像头数量来确保树的所有节点都被监视。
摘要由CSDN通过智能技术生成

一、题目描述

  • 给定一个二叉树,我们在树的节点上安装摄像头。
  • 节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。
  • 计算监控树的所有节点所需的最小摄像头数量。
    在这里插入图片描述

二、理解题意

  • 在本题中,每个节点可以监视父节点、自身以及子节点
  • 要求在所有节点都被监视到时候所需最小的摄像头的数目
  • 摄像头放到非叶子节点上监视的范围最大(父节点+左右子节点)
  • 任意一个节点是否放置摄像头取决于其父节点+左子节点+右子节点的状态
    在这里插入图片描述

三、解法一:暴力搜索

从上往下,依次搜索所有可能性,取最小
在这里插入图片描述

  • interval=2, 则当前节点必须放置摄像头
  • interval=1 + 左右子节点都不存在
    (1)此节点必须放置
  • interval=1 + 左右子节点都存在
    (1)在此节点放置
    (2)放置在左子节点
    (3)放置在右子节点
  • interval=1 + 左右子节点存在一个
    (1)在此节点放置
    (2)放置在左子节点
    (3)放置在右子节点
  • interval=0
    (1)在此节点放置
    (2)放置在左子节点
    (3)放置在右子节点
    在这里插入图片描述
class Solution {
public int minCameraCover(TreeNode root){
	// 注意对于root结点, 如interval=0,表示它的父节点放置了摄像头,
	// root肯定可以不放,不符题意
	return search(root, 1);
}
public int search(TreeNode root, int interval) {// interval表示当前结点(不包含)之上有多少层结点没有放置摄像头
	if(root == null){ return 0; }// 空结点无需放置
	if(interval == 1 && root.left == null && root.right == null){//当没有孩子节点,父节点未放置摄像头,则需放置
		root.val = 1;
		return 1;
	}
	if(interval == 1 && root.left != null && root.right != null){
		// 当有1个间隔,两个孩子,1)此节点放置 2)此节点不放,其左节点放 2)此节点不放,其右节点放
		root.val = 1;
		int x = search(root.left, 0) + search(root.right, 0) + 1;
		root.val = 0;//此节点不放置
		int y = search(root.left, interval) + search(root.right, interval + 1);// 在右结点放置
		int z = search(root.left, interval + 1) + search(root.right, interval);// 在左节点放置
		
		// 对比三种情况 返回最小值
		if(x < Math.min(y, z)){
			root.val = 1;
			return x;
		} else {
			return Math.min(y, z);
		}
	}
	if(interval == 2){//间隔为2,一点要亮灯
		root.val = 1;
		return search(root.left, 0) + search(root.right, 0) + 1;
	}else {
		// 间隔为0 或 间隔为1+左右子树有一个为空
		root.val = 1;//放置此节点
		int x = search(root.left, 0) + search(root.right, 0) + 1;
		root.val = 0;
		// 左右子树都放置(只有一个能放成功,因为必有一个为空)
		int y = search(root.left, interval + 1) + search(root.right, interval + 1);
		
		if(x < y){
			root.val = 1;
			return x;
		} else {
			return y;
		}
	}
	}
}

四、解法二:动态规划

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class Solution {
private final int NOT_COVERED = 0;
private final int HAS_CAMERA = 1;
private final int CVERED = 2;
int res = 0;// 全局变量,表示所需要的摄像头的数量
public int minCameraCover(TreeNode root) {
	if (root == null){ return 0; }
	//如果最后返回的是NOT_COVERED,表示root节点的子节点也没有相机,所以root节点要添加一个相机
	if (dfs(root) == NOT_COVERED){ res++; }
		return res;
}
public int dfs(TreeNode root) {
	//如果是空的,就不需要相机了
	if (root == null){ return CVERED; }//
	int left = dfs(root.left), right = dfs(root.right);
	//如果左右子节点有一个是NOT_COVERED,表示的是子节点既没相机,也没相机覆盖它,所以当前节点需要有一个相机
	if (left == NOT_COVERED || right == NOT_COVERED) {
		//在当前节点放一个相机,统计相机的个数
		res++;
		return HAS_CAMERA;
	}
	//如果左右子节点只要有一个有相机,那么当前节点就不需要相机了,否则返回一个没有相机的标记
	return left == HAS_CAMERA || right == HAS_CAMERA ? CVERED : NOT_COVERED;
}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
监控二叉树问题可以使用动态规划来解决。具体思路如下: 1. 定义状态:使用一个三元组 (u, 0/1, 0/1) 表示当前节点 u 的状态,其中第二个元素表示 u 是否被监控,第三个元素表示 u 的父节点是否被监控。 2. 定义转移:对于一个节点 u,假设其左右子节点分别为 v 和 w,可以分为以下三种情况: a) u 被监控:则 v 和 w 都必须被监控,转移方程为 dp[u][1][0] = dp[v][0][1] + dp[w][0][1] + 1。 b) u 未被监控且其父节点被监控:则 v 和 w 中至少有一个被监控,转移方程为 dp[u][0][1] = min(dp[v][1][0] + dp[w][1][0], dp[v][0][1] + dp[w][1][0], dp[v][1][0] + dp[w][0][1])。 c) u 未被监控且其父节点未被监控:则 v 和 w 中至少有一个被监控,转移方程为 dp[u][0][0] = min(dp[v][1][0] + dp[w][1][0], dp[v][0][1] + dp[w][1][0], dp[v][1][0] + dp[w][0][1], dp[v][0][0] + dp[w][0][0])。 3. 初始状态:对于叶子节点,dp[u][0][0] = 0, dp[u][1][0] = 1, dp[u][0][1] = INF,INF 表示一个足够大的值。 4. 最终答案:对于根节点,dp[root][0][1] 和 dp[root][1][0] 中的较小值即为所求。 下面是使用 Python 代码实现: ``` INF = float('inf') def dfs(u, fa, dp): if not u: return dfs(u.left, u, dp) dfs(u.right, u, dp) dp[u][1][0] = dp[u.left][0][1] + dp[u.right][0][1] + 1 dp[u][0][1] = min(dp[u.left][1][0] + dp[u.right][1][0], dp[u.left][0][1] + dp[u.right][1][0], dp[u.left][1][0] + dp[u.right][0][1]) dp[u][0][0] = min(dp[u.left][1][0] + dp[u.right][1][0], dp[u.left][0][1] + dp[u.right][1][0], dp[u.left][1][0] + dp[u.right][0][1], dp[u.left][0][0] + dp[u.right][0][0]) if fa: dp[u][1][1] = dp[u][0][0] + 1 dp[u][0][1] = min(dp[u][0][1], dp[u][1][0]) else: dp[u][1][1] = dp[u][0][0] + 1 def min_camera_cover(root): dp = {} dfs(root, None, dp) return min(dp[root][1][0], dp[root][0][1]) ``` 其中,root 表示根节点,dp 是一个字典,dp[u][i][j] 表示节点 u 的状态为 (u, i, j) 时的最小监控数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值