#P2027. 2024.9.8-第3题-小红的奇妙树
-
2000ms
Tried: 159
Accepted: 35
Difficulty: 5
所属公司 :
字节
时间 :2024年9月8日
-
算法标签>贪心算法
2024.9.8-第3题-小红的奇妙树
思路:贪心 + 树上dfs
考虑dfs,在自底向上的过程中记录子节点的权值和。
1.如果a[u] < s,至少让a[u] == s,所以ans += s - a[u]
2.如果a[u] >= s,那么u不需要增加权值就能满足条件
代码
java
import java.util.*;
class Main {
static long ans = 0; // 全局变量,用于存储最终的操作次数
static long[] a; // 数组 a 存储每个节点的权值
static List<List<Integer>> edge; // 邻接表表示树结构
// 深度优先搜索函数
static long dfs(int u, int p) {
long s = 0; // 记录子节点的权值和
for (int v : edge.get(u)) { // 遍历当前节点 u 的子节点
if (v == p) continue; // 跳过父节点,避免重复遍历
s += dfs(v, u); // 递归计算子树的权值和
}
// 如果当前节点的权值小于子树权值和
if (a[u] < s) {
ans += s - a[u]; // 增加操作次数
a[u] = s; // 更新当前节点权值为子树权值和
}
s += a[u]; // 将当前节点的权值加入到子树的权值和中
return a[u]; // 返回完整子树的权值和
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(); // 输入节点数量
a = new long[n]; // 初始化权值数组
// 输入每个节点的权值
for (int i = 0; i < n; i++) {
a[i] = sc.nextLong();
}
// 初始化邻接表
edge = new ArrayList<>();
for (int i = 0; i < n; i++) {
edge.add(new ArrayList<>());
}
// 输入树的边,建立树结构
for (int i = 0; i < n - 1; i++) {
int u = sc.nextInt() - 1;
int v = sc.nextInt() - 1;
edge.get(u).add(v); // 添加无向边
edge.get(v).add(u); // 双向边
}
// 从根节点 0 开始dfs,p = -1 表示根节点没有父节点
dfs(0, -1);
// 输出最终的操作次数
System.out.println(ans);
}
}
python
'''
考虑dfs,在自底向上的过程中记录子树的权值和。
1. 如果a[u] < s,至少让a[u] == s,所以ans += s - a[u]
2. 如果a[u] >= s,那么u不需要增加权值就能满足条件
'''
n = int(input()) # 输入节点数量
a = list(map(int, input().split())) # 输入每个节点的权值
edge = [[] for _ in range(n)] # 邻接表初始化
for _ in range(n - 1): # 输入边信息
u, v = map(int, input().split())
edge[u - 1].append(v - 1) # 添加无向边
edge[v - 1].append(u - 1)
ans = 0 # 全局变量存储操作次数
# 深度优先搜索函数
def dfs(u, p):
global ans
s = 0 # 记录子树的权值和
for v in edge[u]: # 遍历当前节点 u 的所有子节点
if v == p: # 跳过父节点,避免重复遍历
continue
s += dfs(v, u) # 递归调用DFS,计算子节点的子树权值和
if a[u] < s: # 如果当前节点的权值小于子树的权值和
ans += s - a[u] # 增加操作次数
a[u] = s # 更新当前节点的权值为子树的权值和
s += a[u] # 将当前节点的权值加到子树权值和中
return s # 返回包含当前节点的完整子树权值和
# 从根节点(0)开始DFS遍历,p=-1表示无父节点
dfs(0, -1)
print(ans) # 输出最终的操作次数
cpp
#include <iostream>
#include <vector>
using namespace std;
#define ll long long
ll ans = 0; // 全局变量,用于存储操作次数
vector<ll> a; // 数组存储每个节点的权值
vector<vector<int>> edge; // 邻接表表示树结构
// 深度优先搜索函数
ll dfs(int u, int p) {
ll s = 0; // 子节点的权值和
for (int v : edge[u]) { // 遍历当前节点 u 的子节点
if (v == p) continue; // 跳过父节点,避免重复遍历
s += dfs(v, u); // 递归调用DFS,计算子树权值和
}
// 如果当前节点的权值小于子树权值和
if (a[u] < s) {
ans += s - a[u]; // 增加操作次数
a[u] = s; // 更新当前节点权值
}
s += a[u]; // 将当前节点的权值加到子树权值和中
return a[u]; // 返回子树的完整权值和
}
int main() {
int n;
cin >> n; // 输入节点数量
a.resize(n); // 初始化节点权值数组
edge.resize(n); // 初始化邻接表
// 输入每个节点的权值
for (int i = 0; i < n; i++) {
cin >> a[i];
}
// 输入树的边,建立树结构
for (int i = 0; i < n - 1; i++) {
int u, v;
cin >> u >> v;
u--; v--; // 转换为0-based索引
edge[u].push_back(v); // 添加无向边
edge[v].push_back(u); // 双向边
}
// 从根节点(0)开始DFS遍历,p=-1表示无父节点
dfs(0, -1);
// 输出最终的操作次数
cout << ans << endl;
return 0;
}
OJ会员可以通过点击题目上方《已通过》查看其他通过代码来学习。
题目内容
小红有一棵n个点的树,其中1号点是根。每个点有一个权值ai。如果满足任意节点的权值大于等于其子节点的权值和,那么这棵树就是一棵奇妙树。 小红每次操作可以选择一个点,将其权值加一。请问小红最少需要多少次操作,才能使这棵树变成一棵奇妙树。
输入描述
第一行一个整数n,表示树的节点数。
第二行n 个整数ai;,表示每个点的权值。
接下来n−1行,每行两个整数u和v,表示u和v之间有一条边。
2≤n≤105
1≤ai≤109
1≤u,v≤n
输出描述
输出一个整数,表示最少需要多少次操作。
样例1
输入
3
1 2 3
1 2
1 3
输出
4
说明
至少需要4次操作,将1号点的权值变为5.