You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

137 lines
4.4 KiB

2 years ago
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<ArrayList<Integer>> 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;
// cut1cut2
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<ArrayList<Integer>> 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];
}
}
}
}