使叶子路径成本相等的最小增量
使叶子路径成本相等的最小增量
第455场周赛2025-06-22
题目
给你一个整数 n,以及一个无向树,该树以节点 0 为根节点,包含 n 个节点,节点编号从 0 到 n - 1。这棵树由一个长度为 n - 1 的二维数组 edges 表示,其中 edges[i] = [ui, vi] 表示节点 ui 和节点 vi 之间存在一条边。
每个节点 i 都有一个关联的成本 cost[i],表示经过该节点的成本。
路径得分 定义为路径上所有节点成本的总和。
你的目标是通过给任意数量的节点 增加 成本(可以增加任意非负值),使得所有从根节点到叶子节点的路径得分 相等 。
返回需要增加成本的节点数的 最小值 。
示例 1:
输入: n = 3, edges = [[0,1],[0,2]], cost = [2,1,3]
输出: 1
解释:
树中有两条从根到叶子的路径:
路径 0 → 1 的得分为 2 + 1 = 3。
路径 0 → 2 的得分为 2 + 3 = 5。
为了使所有路径的得分都等于 5,可以将节点 1 的成本增加 2。
仅需增加一个节点的成本,因此输出为 1。
示例 2:
输入: n = 3, edges = [[0,1],[1,2]], cost = [5,1,4]
输出: 0
解释:
树中只有一条从根到叶子的路径:
路径 0 → 1 → 2 的得分为 5 + 1 + 4 = 10。
由于只有一条路径,所有路径的得分天然相等,因此输出为 0。
示例 3:
输入: n = 5, edges = [[0,4],[0,1],[1,2],[1,3]], cost = [3,4,1,1,7]
输出: 1
解释
树中有三条从根到叶子的路径:
路径 0 → 4 的得分为 3 + 7 = 10。
路径 0 → 1 → 2 的得分为 3 + 4 + 1 = 8。
路径 0 → 1 → 3 的得分为 3 + 4 + 1 = 8。
为了使所有路径的得分都等于 10,可以将节点 1 的成本增加 2。 因此输出为 1。
提示:
2 <= n <= 105
edges.length == n - 1
edges[i] == [ui, vi]
0 <= ui, vi < n
cost.length == n
1 <= cost[i] <= 109
输入保证 edges 表示一棵合法的树。©leetcode
解答
先根据输入构造一棵树,然后在树里递归遍历。
- 构造一棵树:由于只能知道0是根结点,其他边是哪个在上哪个在下不知道,所以可以先把双向的两条边都保存。然后进行层次遍历,先被遍历的那个点上父节点,所以在层次遍历中删除掉从子指向父的边。本来想用visited属性保存,但是由于边只会存在直接父子之间,所以可以在把子节点存入queue的同时删除那条边。
- 在树里递归求最小更改点个数。函数返回最小更改点个数,逻辑是先递归处理了子树,如果子树路径和之间有差,那可以取update和偏小的那个子节点。除了最小更改点个数,还有一个值需要记录,就是从叶节点到当前子节点到路径和,这里实现上为求简单,直接用TreeNode本来的cost属性来保存。
在实现细节上需要注意一个点:由于cost的范围是109,所以cost相加之后会超过int,需要用long保存。
class Solution {
class TreeNode{
List<TreeNode> childs=new ArrayList<>();
long c;//cost
//bool visited;
}
public int minIncrease(int n, int[][] edges, int[] cost) {
// construct Tree
TreeNode root=constructTree(edges,cost);
return minIncrease(root);
}
private int minIncrease(TreeNode root){
//use root.c as path cost of sub tree
int total=0;//node count to increase cost
long max=0;
for(TreeNode c:root.childs){
int tmp=minIncrease(c);
total+=tmp;
}
//find max of childs
for(TreeNode c:root.childs){
if(c.c>max){
max=c.c;
}
}
for(TreeNode c:root.childs){
if(max-c.c>0){
total+=1;//increase child node value
}
}
// System.out.println(root.c+" "+total);
root.c=max+root.c;
return total;
}
private TreeNode constructTree(int[][] edges,int[] cost){…}
}©leetcode
大佬解答
这个解答避开了构造树,直接用List
在递归过程中同时构造父子关系。
这里有两个语法可以学习一下:
//init with empty array
List<Integer>[] g;
Arrays.setAll(g, e -> new java.util.ArrayList<>());
// get max
int mx = Collections.max(list);
code:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
class Solution {
List<Integer>[] g;
int n, a[];
public int minIncrease(int n, int[][] edges, int[] cost) {
this.n = n;
a = cost;
g = new List[n];
Arrays.setAll(g, e -> new java.util.ArrayList<>());
for (var e : edges) {
int x = e[0], y = e[1];
g[x].add(y);
g[y].add(x);
}
dfs(0, -1);
return ans;
}
int ans = 0;
void dfs(int x, int fa) {//第二个参数是父节点,这样避免遍历回去
List<Integer> list = new ArrayList<>();//子节点
for (int y : g[x]) {
if (y == fa) {
continue;
}
dfs(y, x);
list.add(a[y]);
}
if (list.isEmpty()) {
return;
}
int mx = Collections.max(list);
for (int v : list) {
if (v == mx) {
continue;
}
ans++;
}
a[x] += mx;//路径和保存的原来的cost数组
}
}

浙公网安备 33010602011771号