@@ -53,12 +53,14 @@ Tag : 「图论」、「线性 DP」
5353
5454---
5555
56- ### 建图 + DFS
56+ ### 建图 + DFS + 统计答案
5757
5858为了更具有一般性,我们假定该树为多叉树。
5959
6060由于题目给定的 ` parents ` 数组仅支持我们快速查找某个节点的父节点,为了方便遍历整棵树,我们先使用「邻接表」将图(树)建起来。
6161
62+ 还不熟悉「邻接表」存图方式的同学可以看看前置 🧀 : [ 涵盖所有的「存图方式」模板] ( https%3A//mp.weixin.qq.com/s?__biz%3DMzU4NDE3MTEyMA%3D%3D%26mid%3D2247488007%26idx%3D1%26sn%3D9d0dcfdf475168d26a5a4bd6fcd3505d%26chksm%3Dfd9cb918caeb300e1c8844583db5c5318a89e60d8d552747ff8c2256910d32acd9013c93058f%26mpshare%3D1%26scene%3D23%26srcid%3D0311tjKy74JijYzXhHo8Qob7%26sharer_sharetime%3D1646964421353%26sharer_shareid%3D1221771780968b30ef07c3f22cd356ed%2523rd ) 。
63+ 6264然后使用 ` DFS ` 预处理出 ` f ` 数组,其中 $f[ i] $ 代表以节点 $i$ 为根节点的子树所包含的节点数量。
6365
6466考虑如何计算「删除某个节点 $x$ 后,剩余连通块的数量,以及每个连通块的节点数量」,根据节点 $x$ 是否为根节点进行分情况讨论:
@@ -119,6 +121,61 @@ class Solution {
119121
120122---
121123
124+ ### 拓扑排序 + 统计答案
125+ 126+ 通过对「待删除节点是否为根节点」的分情况讨论可知:若要算得删除某个节点后的得分,重点需要知道「当前节点的左右子树(如果有)所包含的节点数量」以及「当前节点的父节点所在连通块的节点数量」。
127+ 128+ 我们解法 1ドル$ 的建图目的就是为了得到某个节点的子树情况,` DFS ` 预处理 ` f ` 数组的目的就是知道以某个节点为根的子树所包含的总节点数量。
129+ 130+ 而这个「建图 + ` DFS ` 」过程可由「拓扑排序」所代替。
131+ 132+ 具体的,我们可以先对 ` parents ` 进行遍历, 统计所有节点的出度 $out[ i] ,ドル然后将所有出度为 0ドル$ 的节点(叶子节点)进行入队。跑一遍求拓扑排序的 ` BFS ` ,每次某个节点 $t$ 出队,我们就对节点 $t$ 的父节点 $fa = parents[ t] $ 进行出度减一操作(若出度减至 0ドル,ドル则将 $fa$ 进行入队,注意 $fa$ 不能为根节点,因为 $fa$ 入队没有意义,其入队不能更新其他点的出度),并在求拓扑序的过程中预处理出 ` l ` 和 ` r ` 数组,$l[ i] $ 和 $r[ i] $ 分别代表节点 $i$ 的左子树的节点数和右节点的节点数。
133+ 134+ 跑完拓扑排序后,我们得到的 ` l ` 和 ` r ` 数组就足够我们统计答案,仍然是对删除节点 $x$ 是否为根节点的分情况讨论:
135+ 136+ * 若 $x$ 不是根节点,得分为 $\max(l[ x] , 1) \times \max( r[ x] , 1)$
137+ * 若 $x$ 为根节点,得分为 $\max(l[ x] , 1) \times \max( r[ x] , 1) \times (n - (l[ x] + r[ x] + 1)$
138+ 139+ 代码:
140+ ``` Java
141+ class Solution {
142+ public int countHighestScoreNodes (int [] parents ) {
143+ int n = parents. length;
144+ int [] out = new int [n];
145+ for (int i = 1 ; i < n; i++ ) out[parents[i]]++ ;
146+ Deque<Integer > d = new ArrayDeque<> ();
147+ for (int i = 0 ; i < n; i++ ) {
148+ if (out[i] == 0 ) d. addLast(i);
149+ }
150+ // l[i] 和 r[i] 分别代表节点 i 的左子树的节点数和右节点的节点数
151+ int [] l = new int [n], r = new int [n];
152+ while (! d. isEmpty()) {
153+ int t = d. pollFirst(), fa = parents[t];
154+ out[fa]-- ;
155+ if (l[fa] == 0 ) l[fa] = l[t] + r[t] + 1 ;
156+ else r[fa] = l[t] + r[t] + 1 ;
157+ if (out[fa] == 0 && fa != 0 ) d. addLast(fa);
158+ }
159+ long max = 0 ;
160+ int ans = 0 ;
161+ for (int i = 0 ; i < n; i++ ) {
162+ long cur = Math . max(l[i], 1 ) * Math . max(r[i], 1 );
163+ if (i != 0 ) cur *= n - (l[i] + r[i] + 1 );
164+ if (cur > max) {
165+ max = cur; ans = 1 ;
166+ } else if (cur == max) {
167+ ans++ ;
168+ }
169+ }
170+ return ans;
171+ }
172+ }
173+ ```
174+ * 时间复杂度:$O(n)$
175+ * 空间复杂度:$O(n)$
176+ 177+ ---
178+ 122179### 最后
123180
124181这是我们「刷穿 LeetCode」系列文章的第 ` No.2049 ` 篇,系列开始于 2021年01月01日,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
0 commit comments