【动态规划】【树形dp】【C++算法】968监控二叉树

简介: 【动态规划】【树形dp】【C++算法】968监控二叉树

作者推荐

【动态规划】【字符串】【表达式】2019. 解出数学表达式的学生分数

本文涉及知识点

动态规划汇总

LeetCode:968监控二叉树

给定一个二叉树,我们在树的节点上安装摄像头。

节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。

计算监控树的所有节点所需的最小摄像头数量。

示例 1:

输入:[0,0,null,0,0]

输出:1

解释:如图所示,一台摄像头足以监控所有节点。

示例 2:

输入:[0,0,null,0,null,0,null,null,0]

输出:2

解释:需要至少两个摄像头来监视树的所有节点。 上图显示了摄像头放置的有效位置之一。

提示:

给定树的节点数的范围是 [1, 1000]。

每个节点的值都是 0。

动态规划

动态规划的状态表示

Rec(root)返回int i1,i2,i3,表示后面三种情况需要的最少摄像头。 i1,表示此子树根节点有摄像头,且子树所有节点都被监控。i2,此子树根节没有摄像头,所有节点都被监控。i3,此子树根节点无摄像头,根节点无监控,除根节点外都被监控。

动态规划的转移方程

叶子节点见初始值,本部分只讨论,非叶子节点:

i3 = min(lefti2)+min(righti2)

i2 ,左节点i1,右节点i1或i2 。或右节点i1,左节点i1,i2不存在。如果右节点不存在,则左节点i1。如果左节点不存在,则右节点i1。

i1 1 + min(lefti1,lefti2,lefti3)+min(righti1,righti2,righti3)

如果当前节点增加摄像头,则左右子树,无轮是否存在,i1,i2,i3 全部是i1。

如果当前节点没摄像头

a,一个子树i1,另外一个子树i1,i2或不存在,都是i2。

b,一个字树i1,另外一个字树i3不合法。

c,没有子树i1。两者都i2。就是i3。

动态规划的初始值

叶子节点i1,i2,i3分别为:1, 1000,0 。 1000或更大的数,表示这种可能不存在。

动态规划的填表顺序

树的先序遍历

动态规划的返回值

root的i1。

代码

核心代码

class Solution {
public:
  int minCameraCover(TreeNode* root) {    
    const auto [i1,i2,i3] =  Rec(root);
    return max(1, min(i1, i2));
  }
  std::tuple<int, int,int> Rec(TreeNode* root)
  {
    if ((nullptr == root->left) && (nullptr == root->right))
    {
      return std::make_tuple(1, 1000, 0);
    }
    int i3 = 0;
    int i21 = 1000,i22=1000;
    int i1 = 1;// 
    int iLeftMin = -1, iRightMin = -1;
    if (nullptr != root->left)
    {
      auto [t1, t2,t3] = Rec(root->left);
      iLeftMin = min(t1, t2);
      i3 += t2;
      i21 = t1;
      i1 += min(t1, min(t2, t3));
    }
    if (nullptr != root->right)
    {
      auto [t1, t2, t3] = Rec(root->right);
      iRightMin = min(t1, t2);
      i3 += t2;
      i22 = t1;
      i1 += min(t1,min(t2, t3));
    } 
    if (nullptr != root->left)
    { 
      i22 += iLeftMin;
    }
    if (nullptr != root->right)
    {     
      i21 += iRightMin;
    }
    const int i2 = min(i21, i22);
    //assert((i1 >= i3) && (i2 >= i3 ));
    std::cout << "val: " << root->val << " i1: " << i1 << " i2:" << i2 << " i3: " << i3 << std::endl;
    return std::make_tuple(i1, i2,i3);
  }
};

测试用例

template<class T>
void Assert(const T& t1, const T& t2)
{
  assert(t1 == t2);
}
template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{
  if (v1.size() != v2.size())
  {
    assert(false);
    return;
  }
  for (int i = 0; i < v1.size(); i++)
  {
    Assert(v1[i], v2[i]);
  }
}
int main()
{ 
  int x,  target;
  const int null = 10000;
  {
    vector<int> nodes = { 1, null, 3, null, 5, null, 7, null, 9, 10, 11, null, null, 14, 15 };
    auto root = NTree::Init(nodes);
    Solution sln;
    auto res = sln.minCameraCover(root);
    Assert(res, 3);
  }
  {
    vector<int> nodes = { 1, 2, null, 4, null, 6, null, null, 9 };
    auto root = NTree::Init(nodes);
    Solution sln;
    auto res = sln.minCameraCover(root);
    Assert(res, 2);
  }
  {
    vector<int> nodes = { 1, 2, null, 3, 4 };
    auto root = NTree::Init(nodes);
    Solution sln;
    auto res = sln.minCameraCover(root);
    Assert(res, 1);
  }
  
  
}

优化

空节点为边界条件,更简洁。

class Solution {
public:
  int minCameraCover(TreeNode* root) {    
    const auto [i1,i2,i3] =  Rec(root);
    return max(1, min(i1, i2));
  }
  std::tuple<int, int,int> Rec(TreeNode* root)
  {
    if (nullptr == root )
    {
      return std::make_tuple(1000,0,0);
    }
    auto [l1,l2,l3] = Rec(root->left);
    auto [r1, r2, r3] = Rec(root->right);
    int i1 = 1 + min(min(l1,l2),l3) + min(min(r1, r2), r3);
    int i2 = min(l1 + r2, r1 + l2);
    i2 = min(i2, l1 + r1);
    int i3 = l2 + r2;
    return std::make_tuple(i1, i2,i3);
  }
};

2023年1月版

struct CStatus

{

int m_iFillAndRootHas;// 覆盖所有节点且根节点有监控

int m_iFill;// 覆盖所有节点,根节点不一定有监控

int m_iFillChild;//覆盖子节点,不一定覆盖根节点

};

class Solution {

public:

int minCameraCover(TreeNode* root) {

CStatus stuatu = dfs(root);

return stuatu.m_iFill;

}

CStatus dfs(TreeNode* root)

{

if (nullptr == root)

{

return{ INT_MAX / 2, 0, 0 };

}

CStatus left = dfs(root->left);

CStatus right = dfs(root->right);

CStatus ret;

ret.m_iFillAndRootHas = 1 + left.m_iFillChild + right.m_iFillChild;

ret.m_iFill = min(ret.m_iFillAndRootHas, min(left.m_iFillAndRootHas+right.m_iFill,right.m_iFillAndRootHas+left.m_iFill));

ret.m_iFillChild = min(ret.m_iFillAndRootHas,left.m_iFill + right.m_iFill);

std::cout << ret.m_iFillAndRootHas << " " << ret.m_iFill << " " << ret.m_iFillChild << std::endl;

return ret;

}

};


相关文章
|
1天前
|
存储 算法 Python
文件管理系统中基于 Python 语言的二叉树查找算法探秘
在数字化时代,文件管理系统至关重要。本文探讨了二叉树查找算法在文件管理中的应用,并通过Python代码展示了其实现过程。二叉树是一种非线性数据结构,每个节点最多有两个子节点。通过文件名的字典序构建和查找二叉树,能高效地管理和检索文件。相较于顺序查找,二叉树查找每次比较可排除一半子树,极大提升了查找效率,尤其适用于海量文件管理。Python代码示例包括定义节点类、插入和查找函数,展示了如何快速定位目标文件。二叉树查找算法为文件管理系统的优化提供了有效途径。
33 5
|
1月前
|
存储 C++
【C++】AVL树
AVL树是一种自平衡二叉搜索树,由Georgy Adelson-Velsky和Evgenii Landis提出。它通过确保任意节点的两子树高度差不超过1来维持平衡,支持高效插入、删除和查找操作,时间复杂度为O(log n)。AVL树通过四种旋转操作(左旋、右旋、左-右旋、右-左旋)来恢复树的平衡状态,适用于需要频繁进行数据操作的场景。
43 2
|
1月前
|
算法
分享一些提高二叉树遍历算法效率的代码示例
这只是简单的示例代码,实际应用中可能还需要根据具体需求进行更多的优化和处理。你可以根据自己的需求对代码进行修改和扩展。
|
1月前
|
存储 缓存 算法
如何提高二叉树遍历算法的效率?
选择合适的遍历算法,如按层次遍历树时使用广度优先搜索(BFS),中序遍历二叉搜索树以获得有序序列。优化数据结构,如使用线索二叉树减少空指针判断,自定义节点类增加辅助信息。利用递归与非递归的特点,避免栈溢出问题。多线程并行遍历提高速度,注意线程安全。缓存中间结果,避免重复计算。预先计算并存储信息,提高遍历效率。综合运用这些方法,提高二叉树遍历算法的效率。
61 5
|
1月前
|
算法 Python
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果;贪心算法在每一步选择局部最优解,追求全局最优;动态规划通过保存子问题的解,避免重复计算,确保全局最优。这三种算法各具特色,适用于不同类型的问题,合理选择能显著提升编程效率。
53 2
|
1月前
|
算法
树的遍历算法有哪些?
不同的遍历算法适用于不同的应用场景。深度优先搜索常用于搜索、路径查找等问题;广度优先搜索则在图的最短路径、层次相关的问题中较为常用;而二叉搜索树的遍历在数据排序、查找等方面有重要应用。
39 2
|
2月前
|
算法
动态规划算法学习三:0-1背包问题
这篇文章是关于0-1背包问题的动态规划算法详解,包括问题描述、解决步骤、最优子结构性质、状态表示和递推方程、算法设计与分析、计算最优值、算法实现以及对算法缺点的思考。
104 2
动态规划算法学习三:0-1背包问题
|
1月前
|
机器学习/深度学习 JSON 算法
二叉树遍历算法的应用场景有哪些?
【10月更文挑战第29天】二叉树遍历算法作为一种基础而重要的算法,在许多领域都有着不可或缺的应用,它为解决各种复杂的问题提供了有效的手段和思路。随着计算机科学的不断发展,二叉树遍历算法也在不断地被优化和扩展,以适应新的应用场景和需求。
47 0
|
2月前
|
算法
动态规划算法学习四:最大上升子序列问题(LIS:Longest Increasing Subsequence)
这篇文章介绍了动态规划算法中解决最大上升子序列问题(LIS)的方法,包括问题的描述、动态规划的步骤、状态表示、递推方程、计算最优值以及优化方法,如非动态规划的二分法。
81 0
动态规划算法学习四:最大上升子序列问题(LIS:Longest Increasing Subsequence)
|
2月前
|
算法
动态规划算法学习二:最长公共子序列
这篇文章介绍了如何使用动态规划算法解决最长公共子序列(LCS)问题,包括问题描述、最优子结构性质、状态表示、状态递归方程、计算最优值的方法,以及具体的代码实现。
179 0
动态规划算法学习二:最长公共子序列