【动态规划】【树形dp】【深度优先搜索】LCP 26. 导航装置

简介: 【动态规划】【树形dp】【深度优先搜索】LCP 26. 导航装置

本文涉及知识点

动态规划汇总

LCP 26. 导航装置

小扣参加的秋日市集景区共有 N 个景点,景点编号为 1~N。景点内设有 −1N−1 条双向道路,使所有景点形成了一个二叉树结构,根结点记为 root,景点编号即为节点值。

由于秋日市集景区的结构特殊,游客很容易迷路,主办方决定在景区的若干个景点设置导航装置,按照所在景点编号升序排列后定义装置编号为 1 ~ M。导航装置向游客发送数据,数据内容为列表 [游客与装置 1 的相对距离,游客与装置 2 的相对距离,…,游客与装置 M 的相对距离]。由于游客根据导航装置发送的信息来确认位置,因此主办方需保证游客在每个景点接收的数据信息皆不相同。请返回主办方最少需要设置多少个导航装置。

示例 1:

输入:root = [1,2,null,3,4]

输出:2

解释:在景点 1、3 或景点 1、4 或景点 3、4 设置导航装置。

示例 2:

输入:root = [1,2,3,4]

输出:1

解释:在景点 3、4 设置导航装置皆可。

提示:

2 <= N <= 50000

二叉树的非空节点值为 1~N 的一个排列。

深度优先搜索

原理

性质一:对根节点为root1的子树,将子树外的装置移到root1,效果一样。此子树任意两个节点n1,n2。装置在x,n1和n2的距离差为:(n1→ \rightarrowroot1→ \rightarrowx)-(n2→ \rightarrowroot1→ \rightarrowx)    ⟺    \iff (n1→ \rightarrowroot1)-(n2→ \rightarrowroot1)    ⟺    \iff 此装置放到root1。当前子树根节点或树外有装置简称树外装置,当前子树根节点的子孙节点有装置称树内装置。

原则一:如果装置数量相同,优先使用树外装置,树外装置点可以共用。

性质二:如果一个子树,有两个子节点,那至少需要一个树内装置。树外装置无法区分左右节点。

性质三:子树各有一个装置,无需树内装置或树外装置,整个子树符合题意。

n1,N1来自左子树,n2,N2来自右子树,N1和N2各有一个装置。反证法证明,假定来两个距离都相等。即:

{ n 1 → p 1 → N 1 = = n 2 → r o o t 1 → N 1 到 N 1 的距离相等 n 1 → r o o t 1 → N 2 = = n 2 → p 2 → N 2 到 N 2 的距离相等 \begin{cases} n1\rightarrow p1 \rightarrow N1 == n2 \rightarrow root1 \rightarrow N1 &到N1的距离相等 \\ n1\rightarrow root1 \rightarrow N2 == n2 \rightarrow p2 \rightarrow N2 &到N2的距离相等 \\ \end{cases}{n1p1N1==n2root1N1n1root1N2==n2p2N2N1的距离相等N2的距离相等

→ { n 1 − > r o o t 1 > n 2 − > r o o t 1 n 1 − > r o o t 1 < n 2 − > r o o t 1 矛盾 \rightarrow \begin{cases} n1->root1 > n2->root1\\ n1->root1 < n2->root1 \\ \end{cases} 矛盾{n1>root1>n2>root1n1>root1<n2>root1矛盾

性质四:子树各有一个装置,无需树内装置或树外装置。当前子树内任意一节点n1,和当前树外任意一节点n2符合题意,N1和N2是两个装置,和n1和n2的最近公共祖先p1,p2。假定距离都相等,不失一般性n1在左枝(或root1):

∵ n 1 → p 1 → N 1 = = n 2 → r o o t 1 → N 1 到 N 1 距离相等 ∵ p 1 → N 1 < r o o t 1 → N 1 ∴ n 1 → p 1 > n 2 → r o o t 1 ∵ n 1 → r o o t 1 > = n 1 → p 1 ∴ n 1 → r o o t 1 > n 2 → r o o t 1 ∴ n 1 → r o o t 1 → N 2 > n 2 → r o o t 1 → N 2 和假定到 N 2 距离相等矛盾 \because n1 \rightarrow p1 \rightarrow N1 == n2 \rightarrow root1 \rightarrow N1 到N1距离相等\\ \because p1 \rightarrow N1 < root1 \rightarrow N1 \\ \therefore n1 \rightarrow p1 > n2 \rightarrow root1 \\ \because n1 \rightarrow root1 >= n1 \rightarrow p1 \\ \therefore n1 \rightarrow root1 > n2 \rightarrow root1\\ \therefore n1 \rightarrow root1 \rightarrow N2 > n2 \rightarrow root1 \rightarrow N2 和假定到N2距离相等矛盾n1p1N1==n2root1N1N1距离相等p1N1<root1N1n1p1>n2root1n1root1>=n1p1n1root1>n2root1n1root1N2>n2root1N2和假定到N2距离相等矛盾

性质五:如果左(右)子树有一个有装置,左右子树符合题意。那么有树外装置就符合题意。

a,任意节点到root1的距离都大于0,所以root1不会和子节点到root1的距离相同。

b,n1来自左子树,n2来自右子树,n1和n2到root1距离相等。不失一般性,假定此装置在左子树节点N1。 N1和n1的公共祖先P。则n1到N1的距离为:n1→ \rightarrowP→ \rightarrowN1 ,n2到N1的距离为:n2→ \rightarrowroot1→ \rightarrowN1 ,有n1 N1都是左子树的节点,所以他们到P的距离小于到root1的距离。{ n 1 → P < n 1 → r o o t 1 P → N 1 < r o o t → N 1 → { n 1 到 N 1 的距离 < n 1 → r o o t 1 → N 1 \begin{cases} n1\rightarrow P < n1 \rightarrow root1 \\ P \rightarrow N1 < root \rightarrow N1 \end{cases}\rightarrow \begin{cases} n1到N1的距离 < n1\rightarrow root1 \rightarrow N1 \end{cases}{n1P<n1root1PN1<rootN1{n1N1的距离<n1root1N1

→ → → → → → → → → → n 1 和 n 2 到 r o o t 1 的距离相同 n 1 到 N 1 的距离 < n 2 → r o o t 1 → N 1 ( n 2 到 N 1 的距离) \Large^{n1和n2到root1的距离相同}_{\rightarrow\rightarrow\rightarrow\rightarrow\rightarrow\rightarrow\rightarrow\rightarrow\rightarrow\rightarrow} \normalsize n1到N1的距离 < n2\rightarrow root1 \rightarrow N1(n2到N1的距离)→→→→→→→→→→n1n2root1的距离相同n1N1的距离<n2root1N1(n2N1的距离)

性质六:如果左(右)子树有一个有装置,左右子树符合题意。那么有树外装置,树内节点和任意节点都符合题意。n1是树内节点,n2是树外节点。N1是树内装置,N2是树外装置。n1和N1的最近公共祖先是p1,n2和N2的最近公共祖先是 p2,n2和root1的最近公共祖先p3。反证发:假定n1和n2到N1和N2的距离相同。即:

n 1 → p 1 → N 1 = = n 2 → p 3 → r o o t 1 → N 1 式子一 n 1 → r o o t 1 → N 2 = = n 2 → p 2 → N 2 式子二 式子一 → n 1 − > r o o t 1 > n 2 → p 3 → r o o t 1 n 1 → r o o t 1 → N 2 > n 2 → p 3 → r o o t 1 → N 2 大于等于 n 2 → N 2 ( n 2 直达 N 2 一定不慢与需要经过 p 3 , r o o t 1 ) n1\rightarrow p1 \rightarrow N1 == n2 \rightarrow p3 \rightarrow root1 \rightarrow N1 式子一 \\ n1 \rightarrow root1 \rightarrow N2 == n2 \rightarrow p2 \rightarrow N2 式子二\\ 式子一 \rightarrow n1->root1> n2 \rightarrow p3 \rightarrow root1 \\ n1 \rightarrow root1 \rightarrow N2 > n2 \rightarrow p3 \rightarrow root1 \rightarrow N2 \\ 大于等于 n2 \rightarrow N2(n2直达N2一定不慢与需要经过p3,root1)n1p1N1==n2p3root1N1式子一n1root1N2==n2p2N2式子二式子一n1>root1>n2p3root1n1root1N2>n2p3root1N2大于等于n2N2(n2直达N2一定不慢与需要经过p3,root1)

性质七:如果左右子树皆无装置,根据性质二,需要一个树内装置。不需要树外装置。

性质八:如果只有一个孩子,且孩子没有树内装置。那说明所有子孙节点都是孤子节点。需要一个树外装置,不需要树内装置。

性质六:如果根节点和树外无装置。一个子树有多个装置,另一个子树无装置。根据性质一,无法满足题目要求。

性质五: 独子树,一个装置就可以搞定,放到叶子节点或根节点。

性质六:非独子树, 如果装置放到树外或根节点,无法区分兄弟节点。如果放到兄弟及其后代上,无法区分兄弟和祖父。树外或根节点必须放到一个,树内任意位置放一个。

处理思路

独子树,返回1。

双子节点,如果后代没有双子节点,则需要新增一个装置。正棵树的根,也要一个装置。

注意: 如果根节点是双子节点忽略,因为他没祖先,无需区分。

两种情况,可以统一为:

1+ 没有后代是双子节点的双子节点。

DFS返回两个值:b1和i2。b1表示是否需要树外节点,i2表示如果有树外节点,树内需要多少节点。

一,叶子节点返回{false,0}

二, 双子节点 并且 没有子树有装置 {true,1} 两棵子树都有双子节点。

{ 单子节点返回 t u r e , 两个子树 i 2 之和 双子节点并且两个子树都有装置 f a l s e , 两个子树 i 2 之和 双子节点并且一个子树都有装置 t r u e , 两个子树 i 2 之和 → i 12 和 i 22 分别表示左只和右支的 i 2 。可以统一为: 0 ! = i 12 ∗ i 22 , 两个子树 i 2 之和 \begin{cases} 单子节点返回{ture,两个子树i2之和} \\ 双子节点 并且 两个子树都有装置 {false,两个子树i2之和} \\ 双子节点 并且 一个子树都有装置 {true,两个子树i2之和} \\ \end{cases}\\ \rightarrow i12 和i22 分别表示左只和右支的i2。可以统一为:{0!=i12*i22,两个子树i2之和}单子节点返回ture,两个子树i2之和双子节点并且两个子树都有装置false,两个子树i2之和双子节点并且一个子树都有装置true,两个子树i2之和i12i22分别表示左只和右支的i2。可以统一为:0!=i12i22,两个子树i2之和

代码

class Solution {
public:
  int navigation(TreeNode* root) {
    const auto [i11, i12] = DFS(root->left);
    const auto [i21, i22] = DFS(root->right);
    if (0 == i12 + i22)
    {
      return 1;
    }
        bool b1 = (i11| i21)&&(0==i12*i22);   
    return  i12 + i22 +b1 ;
  }
  std::pair<bool, int> DFS(TreeNode* root)
  {
    if (nullptr == root)
    {
      return { 0,0 };
    }
    const auto [i11, i12] = DFS(root->left);
    const auto [i21, i22] = DFS(root->right);
    const int iChildCount = i12 + i22;
    bool b2 = (nullptr != root->left) && (nullptr != root->right);
    if (b2)
    {
      return { 0 == i12 * i22,(b2 && (0 == iChildCount)) ? 1 : iChildCount };
    }
    return { i11 | i21, iChildCount };
  }
};


相关文章
|
5天前
|
并行计算 测试技术 区块链
【图论】【堆优化的单源路径】LCP 20. 快速公交
【图论】【堆优化的单源路径】LCP 20. 快速公交
|
5天前
|
算法 测试技术 C++
【动态规划】【前缀和】【C++算法】LCP 57. 打地鼠
【动态规划】【前缀和】【C++算法】LCP 57. 打地鼠
|
5天前
|
测试技术
【动态规划】【状态压缩】LCP04 覆盖
【动态规划】【状态压缩】LCP04 覆盖
|
5天前
|
算法 测试技术 C#
【深度优化】【广度优先】【状态压缩】864. 获取所有钥匙的最短路径
【深度优化】【广度优先】【状态压缩】864. 获取所有钥匙的最短路径
|
5天前
|
算法 测试技术 C#
【二分图】【二分图最大匹配】LCP 04. 覆盖
【二分图】【二分图最大匹配】LCP 04. 覆盖
|
5天前
|
算法 测试技术 C#
【广度优先搜索】【堆】【C++算法】407. 接雨水 II
【广度优先搜索】【堆】【C++算法】407. 接雨水 II
|
5天前
|
机器学习/深度学习 测试技术
【图论】【状态压缩】【树】【深度优先搜索】1617. 统计子树中城市之间最大距离
【图论】【状态压缩】【树】【深度优先搜索】1617. 统计子树中城市之间最大距离
|
5天前
|
算法 Java 测试技术
【广度优先搜索】【网格】【割点】1263. 推箱子
【广度优先搜索】【网格】【割点】1263. 推箱子
|
10月前
|
C语言 C++
【二分查找】LCP 18. 早餐组合
【二分查找】LCP 18. 早餐组合
74 0
|
机器人
(二分)730. 机器人跳跃问题
(二分)730. 机器人跳跃问题
53 0