2025-12-07:使叶子路径成本相等的最小增量。用go语言,给出一个整数 n 和一棵以节点 0 为根的树(节点编号 0 到 n−1)。树用长度为 n−1 的

34 阅读6分钟

2025-12-07:使叶子路径成本相等的最小增量。用go语言,给出一个整数 n 和一棵以节点 0 为根的树(节点编号 0 到 n−1)。树用长度为 n−1 的边数组 edges 表示,其中 edges[i] = [u_i, v_i] 表示节点 u_i 与 v_i 相连。每个节点 i 关联一个代价 cost[i],一条路径的“权重”定义为路径上所有节点代价的和。

允许你对任意若干节点各自增加一个不小于 0 的数(不同节点的增量可以不同);如果某节点增加的值为 0,则视为未改变。目标是通过这样的增量使得从根 0 到所有叶子节点的路径权重都相等。请返回需要被增大的节点的最小数量。

2 <= n <= 100000。

edges.length == n - 1。

edges[i] == [ui, vi]。

0 <= ui, vi < n。

cost.length == n。

1 <= cost[i] <= 1000000000。

输入保证 edges 表示一棵合法的树。

输入: 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。

题目来自力扣3593。

大体步骤

1. 构建树的邻接表

代码首先根据输入的边数组 edges 构建了一个邻接表 g,用于表示树的结构。这是一个无向图,因此每条边都会在邻接表中为两个节点相互添加连接。特别地,代码为根节点0额外添加了一个指向-1的边,这是为了避免在后续处理中将根节点误判为叶子节点。

2. 深度优先搜索(DFS)与贪心调整

核心逻辑在 dfs 函数中,它采用后序遍历(先处理子节点,再处理当前节点)的方式自底向上地计算和调整路径成本。

  • 叶子节点处理:当一个节点是叶子节点时(在邻接表中除了父节点只有一个连接),它到根节点的路径成本就是其自身的 cost。DFS函数会直接返回这个成本值。
  • 非叶子节点处理:对于非叶子节点,DFS会递归地处理其所有子节点,并收集每个子节点传递上来的、从该子节点到其下属叶子节点的最大路径成本mx)。
  • 寻找最大成本与计数:在处理当前节点的所有子节点时,算法会追踪遇到的最大路径成本 maxS,并记录有多少个子节点能提供这个最大成本(cnt)。这个过程确保了算法能识别出当前节点子树下“最长”的路径。
  • 贪心决策与计数:关键的贪心策略在于,为了让所有从当前节点出发到叶子节点的路径成本相等,只需要让那些路径成本较小的子节点向最大的路径成本看齐。具体实现上,算法不会去实际修改节点的 cost 值,而是通过计算需要增加成本的节点数量来解决问题。公式 ans += len(g[x]) - 1 - cnt 的含义是:
    • len(g[x]) - 1:当前节点 x 拥有的有效子节点数量(因为邻接表中包含指向父节点的边,需要减去)。
    • cnt:已经拥有最大路径成本的子节点数量。
    • 两者之差,就是那些路径成本不是最大的子节点数量。对于这些子节点所在的路径,我们需要通过增加其中某个节点的成本来提升整条路径的成本。这个“某个节点”的调整可以等效地上推到当前节点 x 的子节点上。因此,这个差值就是在当前节点层面,需要执行成本增加的节点数量
  • 返回路径成本:最后,DFS函数返回从当前节点 x 到其叶子节点的最大路径成本,即 maxS + cost[x]。这个值将提供给 x 的父节点用于计算。

3. 算法启动

从根节点0开始调用DFS,初始的父节点设为-1。DFS遍历完整棵树后,累加器 ans 中存储的值就是所需的最小增量节点数量,将其返回。

复杂度分析

时间复杂度:O(n)

  • 建图:需要处理 n-1 条边,时间复杂度为 O(n)。
  • DFS遍历:每个节点恰好被访问一次,在每次访问时,处理其所有子节点。虽然每个节点可能有多个子节点,但整棵树的边总数是 n-1,因此所有节点的子节点处理次数总和是 O(n)。DFS的整体时间复杂度为 O(n)。
  • 综上,算法的总时间复杂度为 O(n)

空间复杂度:O(n)

  • 邻接表存储:需要存储 n 个节点的邻接关系,空间复杂度为 O(n)。
  • 递归调用栈:在最坏情况下(树退化为一条链),递归深度为 n,空间复杂度为 O(n)。
  • 综上,算法的总空间复杂度为 O(n)

Go完整代码如下:

package main

import (
	"fmt"
)

func minIncrease(n int, edges [][]int, cost []int) (ans int) {
	g := make([][]int, n)
	for _, e := range edges {
		x, y := e[0], e[1]
		g[x] = append(g[x], y)
		g[y] = append(g[y], x)
	}
	g[0] = append(g[0], -1)

	var dfs func(int, int) int
	dfs = func(x, fa int) (maxS int) {
		cnt := 0
		for _, y := range g[x] {
			if y == fa {
				continue
			}
			mx := dfs(y, x)
			if mx > maxS {
				maxS = mx
				cnt = 1
			} else if mx == maxS {
				cnt++
			}
		}
		ans += len(g[x]) - 1 - cnt
		return maxS + cost[x]
	}
	dfs(0, -1)
	return
}

func main() {
	n := 3
	edges := [][]int{{0, 1}, {0, 2}}
	cost := []int{2, 1, 3}
	result := minIncrease(n, edges, cost)
	fmt.Println(result)
}

在这里插入图片描述

Python完整代码如下:

# -*-coding:utf-8-*-

from typing import List

def minIncrease(n: int, edges: List[List[int]], cost: List[int]) -> int:
    g = [[] for _ in range(n)]
    for x, y in edges:
        g[x].append(y)
        g[y].append(x)
    g[0].append(-1)  # 添加虚拟父节点,方便处理根节点
    
    ans = 0
    
    def dfs(x: int, fa: int) -> int:
        nonlocal ans
        maxS = 0
        cnt = 0
        for y in g[x]:
            if y == fa:
                continue
            mx = dfs(y, x)
            if mx > maxS:
                maxS = mx
                cnt = 1
            elif mx == maxS:
                cnt += 1
        ans += len(g[x]) - 1 - cnt
        return maxS + cost[x]
    
    dfs(0, -1)
    return ans

if __name__ == "__main__":
    n = 3
    edges = [[0, 1], [0, 2]]
    cost = [2, 1, 3]
    result = minIncrease(n, edges, cost)
    print(result)

在这里插入图片描述

C++完整代码如下:

#include <iostream>
#include <vector>
#include <functional>

using namespace std;

int minIncrease(int n, vector<vector<int>>& edges, vector<int>& cost) {
    vector<vector<int>> g(n);

    // 构建邻接表
    for (auto& e : edges) {
        int x = e[0], y = e[1];
        g[x].push_back(y);
        g[y].push_back(x);
    }
    g[0].push_back(-1);  // 添加虚拟父节点,方便处理根节点

    int ans = 0;

    // 使用 function 包装递归函数
    function<int(int, int)> dfs = [&](int x, int fa) -> int {
        int maxS = 0;
        int cnt = 0;

        for (int y : g[x]) {
            if (y == fa) continue;

            int mx = dfs(y, x);
            if (mx > maxS) {
                maxS = mx;
                cnt = 1;
            } else if (mx == maxS) {
                cnt++;
            }
        }

        ans += (int)g[x].size() - 1 - cnt;
        return maxS + cost[x];
    };

    dfs(0, -1);
    return ans;
}

int main() {
    int n = 3;
    vector<vector<int>> edges = {{0, 1}, {0, 2}};
    vector<int> cost = {2, 1, 3};

    int result = minIncrease(n, edges, cost);
    cout << result << endl;

    return 0;
}

在这里插入图片描述