使叶子路径成本相等的最小增量

使叶子路径成本相等的最小增量

第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

解答

先根据输入构造一棵树,然后在树里递归遍历。

  1. 构造一棵树:由于只能知道0是根结点,其他边是哪个在上哪个在下不知道,所以可以先把双向的两条边都保存。然后进行层次遍历,先被遍历的那个点上父节点,所以在层次遍历中删除掉从子指向父的边。本来想用visited属性保存,但是由于边只会存在直接父子之间,所以可以在把子节点存入queue的同时删除那条边。
  2. 在树里递归求最小更改点个数。函数返回最小更改点个数,逻辑是先递归处理了子树,如果子树路径和之间有差,那可以取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[] g保存边的关系,g[i]代表有哪些点上它的邻居。
在递归过程中同时构造父子关系。

这里有两个语法可以学习一下:

//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数组
    }
}
posted @ 2025-06-22 22:54  Fanny123  阅读(242)  评论(0)    收藏  举报