Hard

题目描述

有一个由 n 个节点组成的无向连通树,节点编号从 0 到 n - 1,共有 n - 1 条边。

给你一个长度为 n 的下标从 0 开始的整数数组 nums,其中 nums[i] 表示第 i 个节点的值。另给你一个长度为 n - 1 的二维整数数组 edges,其中 edges[i] = [ai, bi] 表示树中节点 ai 和 bi 之间有一条边。

移除树中的两条不同的边以形成三个连通分量。对于一对移除的边,定义以下步骤:

  1. 分别获取三个分量中每个分量内所有节点值的异或值。
  2. 最大异或值和最小异或值之间的差值就是这一对移除边的分数。

例如,假设三个分量的节点值分别为:[4,5,7]、[1,9] 和 [3,3,3]。三个异或值分别为 4 ^ 5 ^ 7 = 6、1 ^ 9 = 8 和 3 ^ 3 ^ 3 = 3。最大异或值是 8,最小异或值是 3。分数为 8 - 3 = 5。

返回给定树上任意一对边移除的最小分数。

示例 1:

输入:nums = [1,5,5,4,11], edges = [[0,1],[1,2],[1,3],[3,4]]
输出:9

示例 2:

输入:nums = [5,5,2,4,4,2], edges = [[0,1],[1,2],[5,2],[4,3],[1,3]]
输出:0

提示:

  • n == nums.length
  • 3 <= n <= 1000
  • 1 <= nums[i] <= 10^8
  • edges.length == n - 1
  • edges[i].length == 2
  • 0 <= ai, bi < n
  • ai != bi
  • edges 表示一棵有效的树

解题思路

这道题需要移除两条边将树分成三个连通分量,并使得三个分量的异或值的最大值和最小值之间的差值最小。

解题思路:

  1. 枚举第一条边:首先枚举要移除的第一条边,移除后树会分成两个连通分量。

  2. 预处理子树异或值:对于每个节点,计算以该节点为根的子树的所有节点值的异或值。这可以通过DFS一次性计算完成。

  3. 枚举第二条边:在其中一个连通分量中再移除一条边,将其分成两部分。关键是要确保第二条边在第一条边分割出的某个分量内部。

  4. 计算三个分量的异或值

    • 设整棵树的异或值为 total
    • 移除第一条边后,得到两个分量的异或值
    • 移除第二条边后,其中一个分量被进一步分割
  5. 处理分量关系:需要特别注意两条被移除的边之间的位置关系:

    • 如果两条边没有包含关系,则三个分量互不相交
    • 如果一条边在另一条边分割出的子树内,需要正确计算各分量的异或值
  6. 优化策略

    • 使用DFS预处理每个子树的异或值
    • 通过parent数组判断节点间的祖先关系
    • 枚举所有可能的边对组合,计算对应的分数

时间复杂度为O(n²),因为需要枚举所有可能的边对。空间复杂度为O(n),用于存储图结构和子树异或值。

代码实现

class Solution {
public:
    int minimumScore(vector<int>& nums, vector<vector<int>>& edges) {
        int n = nums.size();
        vector<vector<int>> graph(n);
        
        // 构建邻接表
        for (auto& edge : edges) {
            graph[edge[0]].push_back(edge[1]);
            graph[edge[1]].push_back(edge[0]);
        }
        
        vector<int> xorVal(n), parent(n);
        
        // DFS计算每个子树的异或值
        function<void(int, int)> dfs = [&](int u, int p) {
            parent[u] = p;
            xorVal[u] = nums[u];
            for (int v : graph[u]) {
                if (v != p) {
                    dfs(v, u);
                    xorVal[u] ^= xorVal[v];
                }
            }
        };
        
        dfs(0, -1);
        int total = xorVal[0];
        int ans = INT_MAX;
        
        // 枚举第一条边
        for (auto& edge1 : edges) {
            int u1 = edge1[0], v1 = edge1[1];
            if (parent[u1] == v1) swap(u1, v1);
            
            // 移除edge1后,v1子树和剩余部分的异或值
            int xor1 = xorVal[v1];
            int xor2 = total ^ xor1;
            
            // 枚举第二条边
            for (auto& edge2 : edges) {
                if (edge1 == edge2) continue;
                
                int u2 = edge2[0], v2 = edge2[1];
                if (parent[u2] == v2) swap(u2, v2);
                
                int xor3, newXor1, newXor2;
                
                // 检查edge2是否在v1的子树中
                bool inSubtree = false;
                int curr = v2;
                while (curr != -1 && curr != v1) {
                    curr = parent[curr];
                }
                if (curr == v1) inSubtree = true;
                
                if (inSubtree) {
                    // edge2在v1子树中
                    xor3 = xorVal[v2];
                    newXor1 = xor1 ^ xor3;
                    newXor2 = xor2;
                } else {
                    // edge2不在v1子树中
                    xor3 = xorVal[v2];
                    newXor1 = xor1;
                    newXor2 = xor2 ^ xor3;
                }
                
                int maxXor = max({newXor1, newXor2, xor3});
                int minXor = min({newXor1, newXor2, xor3});
                ans = min(ans, maxXor - minXor);
            }
        }
        
        return ans;
    }
};
class Solution:
    def minimumScore(self, nums: List[int], edges: List[List[int]]) -> int:
        n = len(nums)
        graph = [[] for _ in range(n)]
        
        # 构建邻接表
        for u, v in edges:
            graph[u].append(v)
            graph[v].append(u)
        
        xor_val = [0] * n
        parent = [-1] * n
        
        # DFS计算每个子树的异或值
        def dfs(u, p):
            parent[u] = p
            xor_val[u] = nums[u]
            for v in graph[u]:
                if v != p:
                    dfs(v, u)
                    xor_val[u] ^= xor_val[v]
        
        dfs(0, -1)
        total = xor_val[0]
        ans = float('inf')
        
        # 枚举第一条边
        for edge1 in edges:
            u1, v1 = edge1
            if parent[u1] == v1:
                u1, v1 = v1, u1
            
            # 移除edge1后,v1子树和剩余部分的异或值
            xor1 = xor_val[v1]
            xor2 = total ^ xor1
            
            # 枚举第二条边
            for edge2 in edges:
                if edge1 == edge2:
                    continue
                
                u2, v2 = edge2
                if parent[u2] == v2:
                    u2, v2 = v2, u2
                
                # 检查edge2是否在v1的子树中
                in_subtree = False
                curr = v2
                while curr != -1 and curr != v1:
                    curr = parent[curr]
                if curr == v1:
                    in_subtree = True
                
                if in_subtree:
                    # edge2在v1子树中
                    xor3 = xor_val[v2]
                    new_xor1 = xor1 ^ xor3
                    new_xor2 = xor2
                else:
                    # edge2不在v1子树中
                    xor3 = xor_val[v2]
                    new_xor1 = xor1
                    new_xor2 = xor2 ^ xor3
                
                max_xor = max(new_xor1, new_xor2, xor3)
                min_xor = min(new_xor1, new_xor2, xor3)
                ans = min(ans, max_xor - min_xor)
        
        return ans
public class Solution {
    public int MinimumScore(int[] nums, int[][] edges) {
        int n = nums.Length;
        List<int>[] graph = new List<int>[n];
        for (int i = 0; i < n; i++) {
            graph[i] = new List<int>();
        }
        
        // 构建邻接表
        foreach (var edge in edges) {
            graph[edge[0]].Add(edge[1]);
            graph[edge[1]].Add(edge[0]);
        }
        
        int[] xorVal = new int[n];
        int[] parent = new int[n];
        
        // DFS计算每个子树的异或值
        void Dfs(int u, int p) {
            parent[u] = p;
            xorVal[u] = nums[u];
            foreach (int v in graph[u]) {
                if (v != p) {
                    Dfs(v, u);
                    xorVal[u] ^= xorVal[v];
                }
            }
        }
        
        Dfs(0, -1);
        int total = xorVal[0];
        int ans = int.MaxValue;
        
        // 枚举第一条边
        for (int i = 0; i < edges.Length; i++) {
            var edge1 = edges[i];
            int u1 = edge1[0], v1 = edge1[1];
            if (parent[u1] == v1) {
                (u1, v1) = (v1, u1);
            }
            
            // 移除edge1后,v1子树和剩余部分的异或值
            int xor1 = xorVal[v1];
            int xor2 = total ^ xor1;
            
            // 枚举第二条边
            for (int j = 0; j < edges.Length; j++) {
                if (i == j) continue;
                
                var edge2 = edges[j];
                int u2 = edge2[0], v2 = edge2[1];
                if (parent[u2] == v2) {
                    (u2, v2) = (v2, u2);
                }
                
                // 检查edge2是否在v1的子树中
                bool inSubtree = false;
                int curr = v2;
                while (curr != -1 && curr != v1) {
                    curr = parent[curr];
                }
                if (curr == v1) inSubtree = true;
                
                int xor3, newXor1, newXor2;
                if (inSubtree) {
                    // edge2在v1子树中
                    xor3 = xorVal[v2];
                    newXor1 = xor1 ^ xor3;
                    newXor2 = xor2;
                } else {
                    // edge2不在v1子树中
                    xor3 = xorVal[v2];
                    newXor1 = xor1;
                    newXor2 = xor2 ^ xor3;
                }
                
                int maxXor = Math.Max(Math.Max(newXor1, newXor2), xor3);
                int minXor = Math.Min(Math.Min(newXor1, newXor2), xor3);
                ans = Math.Min(ans, maxXor - minXor);
            }
        }
        
        return ans;
    }
}
var minimumScore = function(nums, edges) {
    const n = nums.length;
    const graph = Array.from({length: n}, () => []);
    
    // 构建邻接表
    for (const [u, v] of edges) {
        graph[u].push(v);
        graph[v].push(u);
    }
    
    const xorVal = new Array(n).fill(0);
    const parent = new Array(n).fill(-1);
    
    // DFS计算每个子树的异或值
    function dfs(u, p) {
        parent[u] = p;
        xorVal[u] = nums[u];
        for (const v of graph[u]) {
            if (v !== p) {
                dfs(v, u);
                xorVal[u] ^= xorVal[v];
            }
        }
    }
    
    dfs(0, -1);
    const total = xorVal[0];
    let ans = Infinity;
    
    // 枚举第一条边
    for (let i = 0; i < edges.length; i++) {
        const edge1 = edges[i];
        let [u1, v1] = edge1;
        if (parent[u1]

复杂度分析

指标复杂度
时间-
空间-