|
| 1 | +### 题目描述 |
| 2 | + |
| 3 | +这是 LeetCode 上的 **[2049. 统计最高分的节点数目](https://leetcode-cn.com/problems/count-nodes-with-the-highest-score/solution/gong-shui-san-xie-jian-tu-dfs-by-ac_oier-ujfo/)** ,难度为 **中等**。 |
| 4 | + |
| 5 | +Tag : 「图论」、「线性 DP」 |
| 6 | + |
| 7 | + |
| 8 | + |
| 9 | +给你一棵根节点为 0ドル$ 的 二叉树 ,它总共有 $n$ 个节点,节点编号为 0ドル$ 到 $n - 1$ 。 |
| 10 | + |
| 11 | +同时给你一个下标从 0ドル$ 开始的整数数组 $parents$ 表示这棵树,其中 $parents[i]$ 是节点 $i$ 的父节点。由于节点 0ドル$ 是根,所以 $parents[0] == -1$ 。 |
| 12 | + |
| 13 | +一个子树的 **大小** 为这个子树内节点的数目。每个节点都有一个与之关联的 **分数** 。求出某个节点分数的方法是,将这个节点和与它相连的边全部 **删除** ,剩余部分是若干个 **非空** 子树,这个节点的 **分数** 为所有这些子树 大小的乘积 。 |
| 14 | + |
| 15 | +请你返回有 **最高得分** 节点的 数目 。 |
| 16 | + |
| 17 | +示例 1: |
| 18 | + |
| 19 | +``` |
| 20 | +输入:parents = [-1,2,0,2,0] |
| 21 | + |
| 22 | +输出:3 |
| 23 | + |
| 24 | +解释: |
| 25 | +- 节点 0 的分数为:3 * 1 = 3 |
| 26 | +- 节点 1 的分数为:4 = 4 |
| 27 | +- 节点 2 的分数为:1 * 1 * 2 = 2 |
| 28 | +- 节点 3 的分数为:4 = 4 |
| 29 | +- 节点 4 的分数为:4 = 4 |
| 30 | +最高得分为 4 ,有三个节点得分为 4 (分别是节点 1,3 和 4 )。 |
| 31 | +``` |
| 32 | +示例 2: |
| 33 | + |
| 34 | + |
| 35 | +``` |
| 36 | +输入:parents = [-1,2,0] |
| 37 | + |
| 38 | +输出:2 |
| 39 | + |
| 40 | +解释: |
| 41 | +- 节点 0 的分数为:2 = 2 |
| 42 | +- 节点 1 的分数为:2 = 2 |
| 43 | +- 节点 2 的分数为:1 * 1 = 1 |
| 44 | +最高分数为 2 ,有两个节点分数为 2 (分别为节点 0 和 1 )。 |
| 45 | +``` |
| 46 | + |
| 47 | +提示: |
| 48 | +* $n == parents.length$ |
| 49 | +* 2ドル <= n <= 10^5$ |
| 50 | +* $parents[0] == -1$ |
| 51 | +* 对于 $i != 0$ ,有 0ドル <= parents[i] <= n - 1$ |
| 52 | +* $parents$ 表示一棵二叉树。 |
| 53 | + |
| 54 | +--- |
| 55 | + |
| 56 | +### 建图 + DFS |
| 57 | + |
| 58 | +为了更具有一般性,我们假定该树为多叉树。 |
| 59 | + |
| 60 | +由于题目给定的 `parents` 数组仅支持我们快速查找某个节点的父节点,为了方便遍历整棵树,我们先使用「邻接表」将图(树)建起来。 |
| 61 | + |
| 62 | +然后使用 `DFS` 预处理出 `f` 数组,其中 $f[i]$ 代表以节点 $i$ 为根节点的子树所包含的节点数量。 |
| 63 | + |
| 64 | +考虑如何计算「删除某个节点 $x$ 后,剩余连通块的数量,以及每个连通块的节点数量」,根据节点 $x$ 是否为根节点进行分情况讨论: |
| 65 | + |
| 66 | +* 若 $x$ 为根节点,删除后的连通块的数量为「$x$ 的出边数量」,假定共有 $k$ 条出边,根据题目定义,对应的 **大小** 为各个连通块的节点数量乘积: |
| 67 | +$$ |
| 68 | +f[u_1] \times f[u_2] \times ... \times f[u_k] |
| 69 | +$$ |
| 70 | + |
| 71 | +* 若 $x$ 不是根节点,删除后的连通块的数量为「$x$ 的出边数量 + 1ドル$」,其中 1ドル$ 代指「以 $x$ 节点的父节点所在的整体连通块」。 |
| 72 | + |
| 73 | + 假定节点 $x$ 共有 $k$ 条出边,根据题目定义,对应的 **大小** 为「(各个连通块的节点数量乘积) $\times$ ($x$ 节点的父节点所在的整体连通块大小)」,而「$x$ 节点的父节点所在的整体连通块大小」,利用容斥原理可知为 $f[root] - f[u] = n - f[u],ドル含义为「从原树中减掉以节点 $x$ 为根节点的子树」的部分,即最终 **大小** 为: |
| 74 | +$$ |
| 75 | +(f[u_1] \times f[u_2] \times ... \times f[u_k]) \times (n - f[x]) |
| 76 | +$$ |
| 77 | + |
| 78 | +代码: |
| 79 | +```Java |
| 80 | +class Solution { |
| 81 | + static int N = 100010, M = N * 2; |
| 82 | + static int[] he = new int[N], e = new int[M], ne = new int[M]; |
| 83 | + static int[] f = new int[N]; |
| 84 | + int idx; |
| 85 | + void add(int a, int b) { |
| 86 | + e[idx] = b; |
| 87 | + ne[idx] = he[a]; |
| 88 | + he[a] = idx++; |
| 89 | + } |
| 90 | + public int countHighestScoreNodes(int[] parents) { |
| 91 | + Arrays.fill(he, -1); |
| 92 | + int n = parents.length; |
| 93 | + for (int i = 1; i < n; i++) add(parents[i], i); |
| 94 | + dfs(0); |
| 95 | + long max = 0; |
| 96 | + int ans = 0; |
| 97 | + for (int x = 0; x < n; x++) { |
| 98 | + long cur = 1; |
| 99 | + for (int i = he[x]; i != -1; i = ne[i]) cur *= f[e[i]]; |
| 100 | + if (x != 0) cur *= n - f[x]; |
| 101 | + if (cur > max) { |
| 102 | + max = cur; ans = 1; |
| 103 | + } else if (cur == max) { |
| 104 | + ans++; |
| 105 | + } |
| 106 | + } |
| 107 | + return ans; |
| 108 | + } |
| 109 | + int dfs(int u) { |
| 110 | + int ans = 1; |
| 111 | + for (int i = he[u]; i != -1; i = ne[i]) ans += dfs(e[i]); |
| 112 | + f[u] = ans; |
| 113 | + return ans; |
| 114 | + } |
| 115 | +} |
| 116 | +``` |
| 117 | +* 时间复杂度:建图复杂度为 $O(n)$;通过 `DFS` 预处理 `f` 数组复杂度为 $O(n + m),ドル其中 $m$ 为边数,对于本题(二叉树)而言,点边数量级相等,因此 `DFS` 预处理的复杂度为 $O(n)$;模拟删除任意点并统计答案的复杂度为 $O(n + m),ドル对于本题(二叉树)而言,数量级为 $O(n)$。整体复杂度为 $O(n)$ |
| 118 | +* 空间复杂度:$O(n)$ |
| 119 | + |
| 120 | +--- |
| 121 | + |
| 122 | +### 最后 |
| 123 | + |
| 124 | +这是我们「刷穿 LeetCode」系列文章的第 `No.2049` 篇,系列开始于 2021年01月01日,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。 |
| 125 | + |
| 126 | +在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。 |
| 127 | + |
| 128 | +为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。 |
| 129 | + |
| 130 | +在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。 |
| 131 | + |
0 commit comments