package class_2022_07_1_week; import java.util.ArrayList; // 给定一个棵树 // 树上每个节点都有自己的值,记录在数组nums里 // 比如nums[4] = 10,表示4号点的值是10 // 给定树上的每一条边,记录在二维数组edges里 // 比如edges[8] = {4, 9}表示4和9之间有一条无向边 // 可以保证输入一定是一棵树,只不过边是无向边 // 那么我们知道,断掉任意两条边,都可以把整棵树分成3个部分。 // 假设是三个部分为a、b、c // a部分的值是:a部分所有点的值异或起来 // b部分的值是:b部分所有点的值异或起来 // c部分的值是:c部分所有点的值异或起来 // 请问怎么分割,能让最终的:三个部分中最大的异或值 - 三个部分中最小的异或值,最小 // 返回这个最小的差值 // 测试链接 : https://leetcode.cn/problems/minimum-score-after-removals-on-a-tree/ public class Code02_MinimumScoreAfterRemovalsOnATree { // public static int minDiff(int[] nums, int[][] edges) { // int ans = Integer.MAX_VALUE; // // 计算量,不能超过10的8次方! // // 体系学习班,根据数据量猜解法 // // 边 1000 边^平方 : 10的6次方 // for (int i = 0; i < edges.length; i++) { // for (int j = i + 1; j < edges.length; j++) { // // 在删掉i号边,和删掉j号边的情况下 // // 一定会有三个部分! // // 请告诉我,三个部分 max(异或和) - min(异或和) 差值是多少? // // O(1) // int curDiff = ????? ; // ans = Math.min(ans, curDiff); // } // } // return ans; // } public static int cnt; public static int minimumScore(int[] nums, int[][] edges) { int n = nums.length; // 先建立图 ArrayList> graph = new ArrayList<>(); // 4个点,0、1、2、3 // 0 : {} // 1 : {} // 2 : {} // 3 : {} for (int i = 0; i < n; i++) { graph.add(new ArrayList<>()); } for (int[] edge : edges) { // a,b // graph.get(a).add(b); // graph.get(b).add(a); graph.get(edge[0]).add(edge[1]); graph.get(edge[1]).add(edge[0]); } // 无向边组成的无环图 // 为了方便,就认为0是头 // dfn[i] = ? int[] dfn = new int[n]; // xor[i] 以i为头的整棵树,整体异或的结果是多少? int[] xor = new int[n]; // size[i] 以i为头的整棵树,一共几个点? int[] size = new int[n]; cnt = 1; dfs(nums, graph, 0, dfn, xor, size); int ans = Integer.MAX_VALUE, m = edges.length, cut1, cut2, pre, pos, part1, part2, part3, max, min; for (int i = 0; i < m; i++) { // i,要删掉的第一条边,i号边 // edges[i][0] edges[i][1] dfn 谁大,谁就是删掉之后的树的头!cut1 // a b cut1 // { a, b} // 0 1 int a = edges[i][0]; int b = edges[i][1]; cut1 = dfn[a] < dfn[b] ? b : a; for (int j = i + 1; j < m; j++) { // j, 要删掉的第二条边,j号边 // { c, d} // 0 1 int c = edges[j][0]; int d = edges[j][1]; cut2 = dfn[c] < dfn[d] ? d : c; // cut1,cut2 pre = dfn[cut1] < dfn[cut2] ? cut1 : cut2; pos = pre == cut1 ? cut2 : cut1; // 早 pre 晚 pos part1 = xor[pos]; // pos为头的树,是pre为头的树的子树! if (dfn[pos] < dfn[pre] + size[pre]) { part2 = xor[pre] ^ xor[pos]; part3 = xor[0] ^ xor[pre]; } else { // pos为头的树,不是pre为头的树的子树! part2 = xor[pre]; part3 = xor[0] ^ part1 ^ part2; } max = Math.max(Math.max(part1, part2), part3); min = Math.min(Math.min(part1, part2), part3); ans = Math.min(ans, max - min); } } return ans; } // 所有节点的值,存在nums数组里 // 整个图结构,存在graph里 // 当前来到的是cur号点 // 请把cur为头,整棵树,所有节点的dfn、size、xor填好! // 返回! public static void dfs(int[] nums, ArrayList> graph, int cur, int[] dfn, int[] xor, int[] size) { // 当前节点了!, dfn[cur] = cnt++; // 只是来到了cur的头部! xor[cur] = nums[cur]; size[cur] = 1; // 遍历所有的孩子! for (int next : graph.get(cur)) { // 只有dfn是0的孩子,才是cur在树中的下级!!!! if (dfn[next] == 0) { // cur某个孩子是next dfs(nums, graph, next, dfn, xor, size); // next整棵树的异或和, xor[cur] ^= xor[next]; // next整棵树的size size[cur] += size[next]; } } } }