Skip to content

Commit d530631

Browse files
authored
Merge pull request #1269 from 0xff-dev/2322
Add solution and test-cases for problem 2322
2 parents ba230e7 + 457c4b4 commit d530631

File tree

5 files changed

+166
-26
lines changed

5 files changed

+166
-26
lines changed
20 KB
Loading
16.5 KB
Loading

leetcode/2301-2400/2322.Minimum-Score-After-Removals-on-a-Tree/README.md

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,48 @@
11
# [2322.Minimum Score After Removals on a Tree][title]
22

3-
> [!WARNING|style:flat]
4-
> This question is temporarily unanswered if you have good ideas. Welcome to [Create Pull Request PR](https://github.com/kylesliu/awesome-golang-algorithm)
5-
63
## Description
4+
There is an undirected connected tree with n nodes labeled from `0` to `n - 1` and `n - 1` edges.
5+
6+
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.
7+
8+
Remove two **distinct** edges of the tree to form three connected components. For a pair of removed edges, the following steps are defined:
9+
10+
1. Get the XOR of all the values of the nodes for **each** of the three components respectively.
11+
2. The **difference** between the **largest** XOR value and the **smallest** XOR value is the **score** of the pair.
12+
13+
- 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`.
714

8-
**Example 1:**
15+
Return the **minimum** score of any possible pair of edge removals on the given tree.
16+
17+
**Example 1:**
18+
19+
![1](./1.png)
920

1021
```
11-
Input: a = "11", b = "1"
12-
Output: "100"
22+
Input: nums = [1,5,5,4,11], edges = [[0,1],[1,2],[1,3],[3,4]]
23+
Output: 9
24+
Explanation: The diagram above shows a way to make a pair of removals.
25+
- The 1st component has nodes [1,3,4] with values [5,4,11]. Its XOR value is 5 ^ 4 ^ 11 = 10.
26+
- The 2nd component has node [0] with value [1]. Its XOR value is 1 = 1.
27+
- The 3rd component has node [2] with value [5]. Its XOR value is 5 = 5.
28+
The score is the difference between the largest and smallest XOR value which is 10 - 1 = 9.
29+
It can be shown that no other pair of removals will obtain a smaller score than 9.
1330
```
1431

15-
## 题意
16-
> ...
32+
**Example 2:**
1733

18-
## 题解
34+
![2](./2.png)
1935

20-
### 思路1
21-
> ...
22-
Minimum Score After Removals on a Tree
23-
```go
2436
```
25-
37+
Input: nums = [5,5,2,4,4,2], edges = [[0,1],[1,2],[5,2],[4,3],[1,3]]
38+
Output: 0
39+
Explanation: The diagram above shows a way to make a pair of removals.
40+
- The 1st component has nodes [3,4] with values [4,4]. Its XOR value is 4 ^ 4 = 0.
41+
- The 2nd component has nodes [1,0] with values [5,5]. Its XOR value is 5 ^ 5 = 0.
42+
- The 3rd component has nodes [2,5] with values [2,2]. Its XOR value is 2 ^ 2 = 0.
43+
The score is the difference between the largest and smallest XOR value which is 0 - 0 = 0.
44+
We cannot obtain a smaller score than 0.
45+
```
2646

2747
## 结语
2848

Lines changed: 118 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,121 @@
11
package Solution
22

3-
func Solution(x bool) bool {
4-
return x
3+
import (
4+
"math"
5+
"sort"
6+
)
7+
8+
// minimumScore calculates the minimum score achievable by removing two distinct edges from a tree.
9+
// The score is defined as the difference between the largest and smallest XOR values of the three resulting components.
10+
func Solution(nums []int, edges [][]int) int {
11+
n := len(nums) // Number of nodes
12+
13+
// 1. Build adjacency list for graph traversal
14+
adj := make([][]int, n)
15+
for _, edge := range edges {
16+
u, v := edge[0], edge[1]
17+
adj[u] = append(adj[u], v)
18+
adj[v] = append(adj[v], u) // Undirected graph
19+
}
20+
21+
// 2. Calculate the total XOR sum of all node values
22+
totalXorSum := 0
23+
for _, num := range nums {
24+
totalXorSum ^= num
25+
}
26+
27+
// 3. Prepare slices for DFS pre-calculation
28+
// subtreeXor[i]: XOR sum of nodes in the subtree rooted at i (including i)
29+
subtreeXor := make([]int, n)
30+
// tin[i]: Entry time of node i in DFS traversal
31+
tin := make([]int, n)
32+
// tout[i]: Exit time of node i in DFS traversal (after visiting all its descendants)
33+
tout := make([]int, n)
34+
timer := 0 // Global timer for assigning tin/tout values
35+
36+
// DFS function to compute subtreeXor, tin, and tout
37+
var dfs func(u, p int) // u: current node, p: parent node
38+
dfs = func(u, p int) {
39+
timer++
40+
tin[u] = timer // Mark entry time for node u
41+
42+
subtreeXor[u] = nums[u] // Initialize subtree XOR with current node's value
43+
44+
// Traverse all neighbors of u
45+
for _, v := range adj[u] {
46+
if v == p { // Skip if v is the parent of u
47+
continue
48+
}
49+
dfs(v, u) // Recursively call DFS for child v
50+
subtreeXor[u] ^= subtreeXor[v] // Aggregate child's subtree XOR into current node's
51+
}
52+
53+
timer++
54+
tout[u] = timer // Mark exit time for node u
55+
}
56+
57+
// Start DFS from node 0 (arbitrary root), with -1 as its virtual parent
58+
dfs(0, -1)
59+
60+
// Initialize minimum score to a very large value
61+
minScore := math.MaxInt32
62+
63+
// 4. Enumerate all possible pairs of distinct nodes (i, j)
64+
// These nodes represent the "roots" of the two subtrees that will be cut off.
65+
// The edges being cut are implicitly (parent[i], i) and (parent[j], j).
66+
// We start from node 1 because node 0 is the root and does not have a "parent edge" to cut.
67+
for i := 1; i < n; i++ {
68+
for j := i + 1; j < n; j++ { // j must be different from i
69+
70+
// Check if i is an ancestor of j using DFS timestamps
71+
// (tin[i] < tin[j] and tout[i] > tout[j])
72+
isIAncestorOfJ := (tin[i] < tin[j] && tout[i] > tout[j])
73+
// Check if j is an ancestor of i using DFS timestamps
74+
isJAncestorOfI := (tin[j] < tin[i] && tout[j] > tout[i])
75+
76+
var xorVal1, xorVal2, xorVal3 int // The three component XOR sums
77+
78+
if isIAncestorOfJ {
79+
// Case 1: Node i is an ancestor of node j.
80+
// This means j's subtree is entirely contained within i's subtree.
81+
// The three components are:
82+
// - Component A: The subtree rooted at j. (xorVal1)
83+
// - Component B: The part of i's subtree *outside* j's subtree. (xorVal2)
84+
// - Component C: The rest of the tree (outside i's subtree). (xorVal3)
85+
xorVal1 = subtreeXor[j]
86+
xorVal2 = subtreeXor[i] ^ subtreeXor[j] // XOR sum of i's subtree excluding j's subtree
87+
xorVal3 = totalXorSum ^ subtreeXor[i] // Total XOR sum excluding i's subtree
88+
} else if isJAncestorOfI {
89+
// Case 2: Node j is an ancestor of node i.
90+
// This is symmetric to Case 1. Swap roles of i and j for calculation.
91+
xorVal1 = subtreeXor[i]
92+
xorVal2 = subtreeXor[j] ^ subtreeXor[i] // XOR sum of j's subtree excluding i's subtree
93+
xorVal3 = totalXorSum ^ subtreeXor[j] // Total XOR sum excluding j's subtree
94+
} else {
95+
// Case 3: Nodes i and j are not ancestors of each other.
96+
// Their respective subtrees are disjoint.
97+
// The three components are:
98+
// - Component A: The subtree rooted at i. (xorVal1)
99+
// - Component B: The subtree rooted at j. (xorVal2)
100+
// - Component C: The rest of the tree (outside both i's and j's subtrees). (xorVal3)
101+
xorVal1 = subtreeXor[i]
102+
xorVal2 = subtreeXor[j]
103+
xorVal3 = totalXorSum ^ subtreeXor[i] ^ subtreeXor[j] // Total XOR sum excluding both i's and j's subtrees
104+
}
105+
106+
// Store the three XOR values in a slice, sort them, and calculate the difference.
107+
currentXors := []int{xorVal1, xorVal2, xorVal3}
108+
sort.Ints(currentXors) // Sort to easily find min and max
109+
110+
// Calculate the score for this pair of edge removals
111+
diff := currentXors[2] - currentXors[0] // Largest XOR - Smallest XOR
112+
113+
// Update the minimum score found so far
114+
if diff < minScore {
115+
minScore = diff
116+
}
117+
}
118+
}
119+
120+
return minScore
5121
}

leetcode/2301-2400/2322.Minimum-Score-After-Removals-on-a-Tree/Solution_test.go

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,30 +10,34 @@ func TestSolution(t *testing.T) {
1010
// 测试用例
1111
cases := []struct {
1212
name string
13-
inputs bool
14-
expect bool
13+
nums []int
14+
edges [][]int
15+
expect int
1516
}{
16-
{"TestCase", true, true},
17-
{"TestCase", true, true},
18-
{"TestCase", false, false},
17+
{"TestCase1", []int{1, 5, 5, 4, 11}, [][]int{
18+
{0, 1}, {1, 2}, {1, 3}, {3, 4},
19+
}, 9},
20+
{"TestCase2", []int{5, 5, 2, 4, 4, 2}, [][]int{
21+
{0, 1}, {1, 2}, {5, 2}, {4, 3}, {1, 3},
22+
}, 0},
1923
}
2024

2125
// 开始测试
2226
for i, c := range cases {
2327
t.Run(c.name+" "+strconv.Itoa(i), func(t *testing.T) {
24-
got := Solution(c.inputs)
28+
got := Solution(c.nums, c.edges)
2529
if !reflect.DeepEqual(got, c.expect) {
26-
t.Fatalf("expected: %v, but got: %v, with inputs: %v",
27-
c.expect, got, c.inputs)
30+
t.Fatalf("expected: %v, but got: %v, with inputs: %v %v",
31+
c.expect, got, c.nums, c.edges)
2832
}
2933
})
3034
}
3135
}
3236

33-
// 压力测试
37+
// 压力测试
3438
func BenchmarkSolution(b *testing.B) {
3539
}
3640

37-
// 使用案列
41+
// 使用案列
3842
func ExampleSolution() {
3943
}

0 commit comments

Comments
 (0)