为了更好的阅读体检,可以查看我的算法学习博客
在线评测链接:P1082
题目内容
在一个遥远的国度,有一个古老的神秘森林,被认为是森林之王的家园。传说森林之王是一只拥有巨大力量和智慧的生物,掌管着整个森林的命运。为了探索森林之王的秘密,许多勇敢的探险家一直在进入这片神秘的森林中。然而,进入森林之后,他们都没有回来过,因此这个秘密依然没有被解开。
有一天,一位名叫塔子哥的年轻探险家也进入了森林。他翻越了陡峭的山峰,穿过了茂密的丛林,终于到达了一处古老的废墟。在废墟的中心,塔子哥发现了一棵神奇的二叉树。这棵二叉树是如此的美丽,以至于塔子哥不禁驻足观赏,他想知道这棵二叉树有多少个节点满足以该节点为根的子树是满二叉树。于是,他开始了他的计算,希望能够揭开这个森林之王的秘密。
我们定义一棵树是满二叉树,当且仅当每一层的节点数量都达到了最大值(即无法在这一层添加新节点)。
输入描述
第一行输入一个正整数 n n n,代表节点的数量。
接下来的 n n n行,第i行输入两个整数 l i l_{i} li和 r i r_{i} ri,代表 i i i号节点的左儿子和右儿子。请注意,如果一个节点没有左儿子/右儿子,则对应的 l i l_{i} li/ r i r_{i} ri为- 1 1 1。
1 ≤ n ≤ 1 0 5 1\le n\le 10^5 1≤n≤105
输出描述
子树为满二又树的节点数量。
样例
输入
4
2 -1
3 4
-1 -1
-1 -1
输出
3
思路
简单树上dp
根据满二叉树的定义:左右儿子是相同高度的满二叉树 或者 叶子节点。
状态:定义 d p [ i ] dp[i] dp[i] 代表以 i i i为根的子树是多少高度的满二叉树.(不是满二叉树时 d p [ i ] = 0 dp[i] = 0 dp[i]=0).
转移:
d
p
[
i
]
=
{
1
i
i
s
l
e
a
f
d
p
[
l
]
+
1
d
p
[
l
]
≠
0
∧
d
p
[
r
]
≠
0
∧
d
p
[
l
]
=
d
p
[
r
]
∧
d
p
[
l
]
≠
0
0
o
t
h
e
r
s
\large dp[i] =\left\{\begin{matrix} 1& i\quad is\quad leaf\\ dp[l] + 1 & dp[l] \neq 0 \wedge dp[r] \neq 0 \wedge dp[l] =dp[r] \wedge dp[l] \neq 0\\ 0 & others \end{matrix}\right.
dp[i]=⎩
⎨
⎧1dp[l]+10iisleafdp[l]=0∧dp[r]=0∧dp[l]=dp[r]∧dp[l]=0others
t
o
p
−
d
o
w
n
top-down
top−down地求一遍
d
p
dp
dp值即可。最后答案为
a
n
s
=
∑
i
=
1
n
[
d
p
[
i
]
≠
0
]
ans = \large \sum_{i=1}^{n} [dp[i] \neq 0]
ans=i=1∑n[dp[i]=0]
注意,题目没有规定根。所以需要在读图的时候统计一下入度。接着寻找入度为0的点来当作根。
类似题目推荐
本题是比较简单的树上 d p dp dp。这里提供更多习题
LeetCode
CodeFun2000
难度依次递增
P1141 2023.04.01 美团春招-第五题-染色の树
P1190 2023.04.12-华为实习笔试-第二题-获取最多食物
P1196 2023-04-19-华为实习笔试-第二题-塔子哥出城
P1193 2023.04.13-腾讯音乐-暑期实习-第二题-价值二叉树
P1081 2023.3.13-百度-第三题-树上同色连通块
代码
CPP
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;
int e[maxn][2] , d[maxn] , dp[maxn];
void dfs (int u){
// l , r为左右儿子节点
int l = e[u][0];
int r = e[u][1];
// 转移case 1,也是边界条件
if (l == -1 && r == -1) {
dp[u] = 1;
return;
}
if (l != -1) dfs(l);
if (r != -1) dfs(r);
// 转移case 2
if (l != -1 && r != -1 && dp[l] == dp[r] && dp[l] != 0) dp[u] = dp[l] + 1;
// 转移case 3
else dp[u] = 0;
}
int main() {
int n;
cin >> n;
// 读入图
for (int i = 1 ; i <= n ; i++){
cin >> e[i][0] >> e[i][1];
// d用来记录度数
if (e[i][0] != -1) d[e[i][0]]++;
if (e[i][1] != -1) d[e[i][1]]++;
}
// 寻找根
int rt = -1;
for (int i = 1 ; i <= n ; i++)
if (d[i] == 0)
rt = i;
// 从根开始dfs
dfs(rt);
// 求答案
int ans = 0;
for (int i = 1 ; i <= n ; i++)
ans += (dp[i] != 0);
cout << ans << endl;
return 0;
}
python
maxn = 10**5 + 5
n = int(input())
# 初始化边的数组和度数的数组
e = [[0, 0] for i in range(maxn)]
d = [0 for i in range(maxn)]
# 读入边和计算度数
for i in range(1, n + 1):
e[i][0], e[i][1] = map(int, input().split())
if e[i][0] != -1: d[e[i][0]] += 1
if e[i][1] != -1: d[e[i][1]] += 1
rt = -1
for i in range(1, n + 1):
if d[i] == 0:
rt = i
# dfs 过程
dp = [0 for i in range(maxn)]
def dfs(u):
l, r = e[u]
if l == -1 and r == -1:
dp[u] = 1
return
if l != -1: dfs(l)
if r != -1: dfs(r)
if l != -1 and r != -1 and dp[l] == dp[r] and dp[l] != 0:
dp[u] = dp[l] + 1
else:
dp[u] = 0
dfs(rt)
# 计算答案
ans = 0
for i in range(1, n + 1):
if dp[i] != 0: ans += 1
print(ans)
Java
import java.util.*;
class TreeNode{
int val;
TreeNode left;
TreeNode right;
public TreeNode(int val){
this.val = val;
}
}
public class Main {
//记忆化数组,-2代表未记录,-1代表当前结点对应的子树不是完全二叉树
//否则代表当前是完全二叉树,且值为当前完全二叉树的深度,叶子节点深度为1
static int[] memo;
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
memo = new int[n];
Arrays.fill(memo, -2);
TreeNode[] nodes = new TreeNode[n];
for(int i=0;i<n;i++) nodes[i] = new TreeNode(i);
// 输入为两个子节点的可以定义节点建树,就不计算入度找根节点
for(int i=0;i<n;i++){
int l = in.nextInt();
int r = in.nextInt();
if(l!=-1) nodes[i].left = nodes[l-1];
if(r!=-1) nodes[i].right = nodes[r-1];
}
int result = 0;
for(int i=0;i<n;i++){
if(isFull(nodes[i]) >= 0) result++;
}
System.out.println(result);
}
public static int isFull(TreeNode node){
if(node == null) return 0;
if(memo[node.val] != -2) return memo[node.val];
int left = isFull(node.left);
int right = isFull(node.right);
if(left == -1 || right == -1 || left != right){
memo[node.val] = -1;
}else{
memo[node.val] = left + 1;
}
return memo[node.val];
}
}
Go
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
sc := bufio.NewReader(os.Stdin)
var n int
fmt.Fscanln(sc, &n)
nodes := make([][]int, n+1)
for i := 1; i <= n; i++ {
var l, r int
fmt.Fscanln(sc, &l, &r)
nodes[i] = []int{l, r}
}
test3(n, nodes)
}
func test3(n int, nodes [][]int) {
deeps := make([]int, n+1)
var dfs func(node int)
dfs = func(node int) {
left, right := nodes[node][0], nodes[node][1]
// 当前节点为叶节点
if left == -1 && right == -1 {
deeps[node] = 1
return
}
// 当前节点只有左子树或右子树
if left == -1 {
deeps[node] = -1
dfs(right)
return
}
if right == -1 {
deeps[node] = -1
dfs(left)
return
}
// 当前节点的左右子树还未搜索
if deeps[left] == 0 {
dfs(left)
}
if deeps[right] == 0 {
dfs(right)
}
// 当前节点左右子树都搜索过了, 且左右子树不都为满二叉树
if deeps[left] == -1 || deeps[right] == -1 {
deeps[node] = -1
return
}
// 当前节点的左右子树均为满二叉树, 且高度一致
if deeps[left] == deeps[right] {
deeps[node] = deeps[left] + 1
} else {
deeps[node] = -1
}
return
}
dfs(1)
res := 0
for i := 1; i < len(deeps); i++ {
if deeps[i] > 0 {
res++
}
}
fmt.Println(res)
}
// by chuyu
Js
process.stdin.resume();
process.stdin.setEncoding('utf-8');
let input = '';
process.stdin.on('data', (data) => {
input += data;
return;
});
process.stdin.on('end', () => {
const lines = input.trim().split('\n');
const maxn = 100005;
const e = new Array(maxn).fill(0).map(() => new Array(2).fill(0));
const d = new Array(maxn).fill(0);
let dp = new Array(maxn).fill(0);
let n = parseInt(lines[0].trim());
// 读入边和计算度数
for (let i = 1; i <= n; i++) {
const [u, v] = lines[i].trim().split(' ').map(Number);
e[i][0] = u;
e[i][1] = v;
if (u !== -1) d[u]++;
if (v !== -1) d[v]++;
}
let rt = -1;
for (let i = 1; i <= n; i++) {
if (d[i] === 0) {
rt = i;
break;
}
}
function dfs(u) {
let l = e[u][0];
let r = e[u][1];
if (l === -1 && r === -1) {
dp[u] = 1;
return;
}
if (l !== -1) dfs(l);
if (r !== -1) dfs(r);
if (l !== -1 && r !== -1 && dp[l] === dp[r] && dp[l] !== 0) {
dp[u] = dp[l] + 1;
} else {
dp[u] = 0;
}
}
dfs(rt);
// 计算答案
let ans = 0;
for (let i = 1; i <= n; i++) {
if (dp[i] !== 0) ans++;
}
console.log(ans);
});
题目内容均收集自互联网,如如若此项内容侵犯了原著者的合法权益,可联系我: (CSDN网站注册用户名: 塔子哥学算法) 进行删除。