diff --git a/leetcode/2301-2400/2322.Minimum-Score-After-Removals-on-a-Tree/1.png b/leetcode/2301-2400/2322.Minimum-Score-After-Removals-on-a-Tree/1.png new file mode 100644 index 000000000..b25151a62 Binary files /dev/null and b/leetcode/2301-2400/2322.Minimum-Score-After-Removals-on-a-Tree/1.png differ diff --git a/leetcode/2301-2400/2322.Minimum-Score-After-Removals-on-a-Tree/2.png b/leetcode/2301-2400/2322.Minimum-Score-After-Removals-on-a-Tree/2.png new file mode 100644 index 000000000..854d7dd00 Binary files /dev/null and b/leetcode/2301-2400/2322.Minimum-Score-After-Removals-on-a-Tree/2.png differ diff --git a/leetcode/2301-2400/2322.Minimum-Score-After-Removals-on-a-Tree/README.md b/leetcode/2301-2400/2322.Minimum-Score-After-Removals-on-a-Tree/README.md index 0bc3f2043..d74d077d7 100755 --- a/leetcode/2301-2400/2322.Minimum-Score-After-Removals-on-a-Tree/README.md +++ b/leetcode/2301-2400/2322.Minimum-Score-After-Removals-on-a-Tree/README.md @@ -1,28 +1,48 @@ # [2322.Minimum Score After Removals on a Tree][title] -> [!WARNING|style:flat] -> This question is temporarily unanswered if you have good ideas. Welcome to [Create Pull Request PR](https://github.com/kylesliu/awesome-golang-algorithm) - ## Description +There is an undirected connected tree with n nodes labeled from `0` to `n - 1` and `n - 1` edges. + +You are given a **0-indexed** integer array `nums` of length `n` where `nums[i]` represents the value of the `ith` node. You are also given a 2D integer array `edges` of length `n - 1` where `edges[i] = [ai, bi]` indicates that there is an edge between nodes `ai` and `bi` in the tree. + +Remove two **distinct** edges of the tree to form three connected components. For a pair of removed edges, the following steps are defined: + +1. Get the XOR of all the values of the nodes for **each** of the three components respectively. +2. The **difference** between the **largest** XOR value and the **smallest** XOR value is the **score** of the pair. + +- For example, say the three components have the node values: `[4,5,7], [1,9], and [3,3,3]`. The three XOR values are `4 ^ 5 ^ 7 = 6`, `1 ^ 9 = 8`, and `3 ^ 3 ^ 3 = 3`. The largest XOR value is `8` and the smallest XOR value is `3`. The score is then `8 - 3 = 5`. -**Example 1:** +Return the **minimum** score of any possible pair of edge removals on the given tree. + +**Example 1:** + +![1](./1.png) ``` -Input: a = "11", b = "1" -Output: "100" +Input: nums = [1,5,5,4,11], edges = [[0,1],[1,2],[1,3],[3,4]] +Output: 9 +Explanation: The diagram above shows a way to make a pair of removals. +- The 1st component has nodes [1,3,4] with values [5,4,11]. Its XOR value is 5 ^ 4 ^ 11 = 10. +- The 2nd component has node [0] with value [1]. Its XOR value is 1 = 1. +- The 3rd component has node [2] with value [5]. Its XOR value is 5 = 5. +The score is the difference between the largest and smallest XOR value which is 10 - 1 = 9. +It can be shown that no other pair of removals will obtain a smaller score than 9. ``` -## 题意 -> ... +**Example 2:** -## 题解 +![2](./2.png) -### 思路1 -> ... -Minimum Score After Removals on a Tree -```go ``` - +Input: nums = [5,5,2,4,4,2], edges = [[0,1],[1,2],[5,2],[4,3],[1,3]] +Output: 0 +Explanation: The diagram above shows a way to make a pair of removals. +- The 1st component has nodes [3,4] with values [4,4]. Its XOR value is 4 ^ 4 = 0. +- The 2nd component has nodes [1,0] with values [5,5]. Its XOR value is 5 ^ 5 = 0. +- The 3rd component has nodes [2,5] with values [2,2]. Its XOR value is 2 ^ 2 = 0. +The score is the difference between the largest and smallest XOR value which is 0 - 0 = 0. +We cannot obtain a smaller score than 0. +``` ## 结语 diff --git a/leetcode/2301-2400/2322.Minimum-Score-After-Removals-on-a-Tree/Solution.go b/leetcode/2301-2400/2322.Minimum-Score-After-Removals-on-a-Tree/Solution.go index d115ccf5e..775495e53 100644 --- a/leetcode/2301-2400/2322.Minimum-Score-After-Removals-on-a-Tree/Solution.go +++ b/leetcode/2301-2400/2322.Minimum-Score-After-Removals-on-a-Tree/Solution.go @@ -1,5 +1,121 @@ package Solution -func Solution(x bool) bool { - return x +import ( + "math" + "sort" +) + +// minimumScore calculates the minimum score achievable by removing two distinct edges from a tree. +// The score is defined as the difference between the largest and smallest XOR values of the three resulting components. +func Solution(nums []int, edges [][]int) int { + n := len(nums) // Number of nodes + + // 1. Build adjacency list for graph traversal + adj := make([][]int, n) + for _, edge := range edges { + u, v := edge[0], edge[1] + adj[u] = append(adj[u], v) + adj[v] = append(adj[v], u) // Undirected graph + } + + // 2. Calculate the total XOR sum of all node values + totalXorSum := 0 + for _, num := range nums { + totalXorSum ^= num + } + + // 3. Prepare slices for DFS pre-calculation + // subtreeXor[i]: XOR sum of nodes in the subtree rooted at i (including i) + subtreeXor := make([]int, n) + // tin[i]: Entry time of node i in DFS traversal + tin := make([]int, n) + // tout[i]: Exit time of node i in DFS traversal (after visiting all its descendants) + tout := make([]int, n) + timer := 0 // Global timer for assigning tin/tout values + + // DFS function to compute subtreeXor, tin, and tout + var dfs func(u, p int) // u: current node, p: parent node + dfs = func(u, p int) { + timer++ + tin[u] = timer // Mark entry time for node u + + subtreeXor[u] = nums[u] // Initialize subtree XOR with current node's value + + // Traverse all neighbors of u + for _, v := range adj[u] { + if v == p { // Skip if v is the parent of u + continue + } + dfs(v, u) // Recursively call DFS for child v + subtreeXor[u] ^= subtreeXor[v] // Aggregate child's subtree XOR into current node's + } + + timer++ + tout[u] = timer // Mark exit time for node u + } + + // Start DFS from node 0 (arbitrary root), with -1 as its virtual parent + dfs(0, -1) + + // Initialize minimum score to a very large value + minScore := math.MaxInt32 + + // 4. Enumerate all possible pairs of distinct nodes (i, j) + // These nodes represent the "roots" of the two subtrees that will be cut off. + // The edges being cut are implicitly (parent[i], i) and (parent[j], j). + // We start from node 1 because node 0 is the root and does not have a "parent edge" to cut. + for i := 1; i < n; i++ { + for j := i + 1; j < n; j++ { // j must be different from i + + // Check if i is an ancestor of j using DFS timestamps + // (tin[i] < tin[j] and tout[i] > tout[j]) + isIAncestorOfJ := (tin[i] < tin[j] && tout[i] > tout[j]) + // Check if j is an ancestor of i using DFS timestamps + isJAncestorOfI := (tin[j] < tin[i] && tout[j] > tout[i]) + + var xorVal1, xorVal2, xorVal3 int // The three component XOR sums + + if isIAncestorOfJ { + // Case 1: Node i is an ancestor of node j. + // This means j's subtree is entirely contained within i's subtree. + // The three components are: + // - Component A: The subtree rooted at j. (xorVal1) + // - Component B: The part of i's subtree *outside* j's subtree. (xorVal2) + // - Component C: The rest of the tree (outside i's subtree). (xorVal3) + xorVal1 = subtreeXor[j] + xorVal2 = subtreeXor[i] ^ subtreeXor[j] // XOR sum of i's subtree excluding j's subtree + xorVal3 = totalXorSum ^ subtreeXor[i] // Total XOR sum excluding i's subtree + } else if isJAncestorOfI { + // Case 2: Node j is an ancestor of node i. + // This is symmetric to Case 1. Swap roles of i and j for calculation. + xorVal1 = subtreeXor[i] + xorVal2 = subtreeXor[j] ^ subtreeXor[i] // XOR sum of j's subtree excluding i's subtree + xorVal3 = totalXorSum ^ subtreeXor[j] // Total XOR sum excluding j's subtree + } else { + // Case 3: Nodes i and j are not ancestors of each other. + // Their respective subtrees are disjoint. + // The three components are: + // - Component A: The subtree rooted at i. (xorVal1) + // - Component B: The subtree rooted at j. (xorVal2) + // - Component C: The rest of the tree (outside both i's and j's subtrees). (xorVal3) + xorVal1 = subtreeXor[i] + xorVal2 = subtreeXor[j] + xorVal3 = totalXorSum ^ subtreeXor[i] ^ subtreeXor[j] // Total XOR sum excluding both i's and j's subtrees + } + + // Store the three XOR values in a slice, sort them, and calculate the difference. + currentXors := []int{xorVal1, xorVal2, xorVal3} + sort.Ints(currentXors) // Sort to easily find min and max + + // Calculate the score for this pair of edge removals + diff := currentXors[2] - currentXors[0] // Largest XOR - Smallest XOR + + // Update the minimum score found so far + if diff < minScore { + minScore = diff + } + } + } + + return minScore } diff --git a/leetcode/2301-2400/2322.Minimum-Score-After-Removals-on-a-Tree/Solution_test.go b/leetcode/2301-2400/2322.Minimum-Score-After-Removals-on-a-Tree/Solution_test.go index 14ff50eb4..d41e511cc 100644 --- a/leetcode/2301-2400/2322.Minimum-Score-After-Removals-on-a-Tree/Solution_test.go +++ b/leetcode/2301-2400/2322.Minimum-Score-After-Removals-on-a-Tree/Solution_test.go @@ -10,30 +10,34 @@ func TestSolution(t *testing.T) { // 测试用例 cases := []struct { name string - inputs bool - expect bool + nums []int + edges [][]int + expect int }{ - {"TestCase", true, true}, - {"TestCase", true, true}, - {"TestCase", false, false}, + {"TestCase1", []int{1, 5, 5, 4, 11}, [][]int{ + {0, 1}, {1, 2}, {1, 3}, {3, 4}, + }, 9}, + {"TestCase2", []int{5, 5, 2, 4, 4, 2}, [][]int{ + {0, 1}, {1, 2}, {5, 2}, {4, 3}, {1, 3}, + }, 0}, } // 开始测试 for i, c := range cases { t.Run(c.name+" "+strconv.Itoa(i), func(t *testing.T) { - got := Solution(c.inputs) + got := Solution(c.nums, c.edges) if !reflect.DeepEqual(got, c.expect) { - t.Fatalf("expected: %v, but got: %v, with inputs: %v", - c.expect, got, c.inputs) + t.Fatalf("expected: %v, but got: %v, with inputs: %v %v", + c.expect, got, c.nums, c.edges) } }) } } -// 压力测试 +// 压力测试 func BenchmarkSolution(b *testing.B) { } -// 使用案列 +// 使用案列 func ExampleSolution() { }