-题目大意概述-
给定一棵树,有n个节点值,和n-1条边,求在删掉2条边的情况下,三颗连通树各自所有节点值的异或值,最终需要求得给出三个值中的“最大值-最小值”
-思路概述-
仅针对一颗树而言,求这颗树中所有节点的异或值,不难想出从根节点向下dfs的基本框架,进一步的,为了减少“对树异或值重复查询”的现象,可以在递归过程中,将子节点的异或值统一保存至根节点中
考虑到因删边而存在的子树的情况,结合异或性质:a^b^a==b,可以采用类似前缀和方式完成对于其他子树的异或值计算,即子树2==树^子树1
由于要删2条边,多颗子树的父子关系对最终答案的判断有所影响,在此需要分类讨论:1、子树1是子树2的子树 2、子树1是子树2的子树 3、子树1和子树2互为兄弟
判断子树间的父子关系,可以采用dfs序的方法,考虑到dfs的遍历顺序,不难发现父节点的进入遍历的时间早于子节点,而退出遍历的时间却晚于子节点,通过对根结点进入退出时间的记录比较,可以快速的判断子树间的父子关系
-代码实现-
class Solution {
public:
int minimumScore(vector<int>& nums, vector<vector<int>>& edges) {
int n=nums.size();//结点数量
//存无向边
vector<vector<int>>edge(n+10);
for(int i=0;i<n-1;i++){
int u,v;
u=edges[i][0];
v=edges[i][1];
edge[u].push_back(v);
edge[v].push_back(u);
}
//进入&退出时间记录
vector<int>in_cnt(n+10);
vector<int>out_cnt(n+10);
int cnt=1;
in_cnt[0]=1;
vector<int>dis(n+10);//根节点保存子树异或值
for(int i=0;i<n;i++)dis[i]=nums[i];//初始化
function<void(int,int)>dfs=[&](int x,int fa){
for(int i=0;i<edge[x].size();i++){
int v=edge[x][i];
if(v!=fa){
cnt++;
in_cnt[v]=cnt;
dfs(v,x);
dis[x]=dis[x]^dis[v];
}
}
cnt++;
out_cnt[x]=cnt;
};
dfs(0,-1);
//删边枚举
int ans=0xfffffff;
for(int i=0;i<n-1;i++){
for(int j=0;j<n-1;j++){
if(i==j)continue;
int u,v,x,y;
//对于单条边,判断俩节点间的深度
//更深的为子树的根节点
u=edges[i][0];
v=edges[i][1];
if(in_cnt[u]<in_cnt[v])x=v;
else x=u;
u=edges[j][0];
v=edges[j][1];
if(in_cnt[u]<in_cnt[v])y=v;
else y=u;
//情况枚举(画图可知ans表达式)
int sum1,sum2,sum3;
if((in_cnt[x]<in_cnt[y])&&(out_cnt[x]>out_cnt[y])){
sum1=dis[0]^dis[x];
sum2=dis[x]^dis[y];
sum3=dis[y];
ans=min(ans,max(sum1,max(sum2,sum3))-min(sum1,min(sum2,sum3)));
}
else if((in_cnt[x]>in_cnt[y])&&(out_cnt[x]<out_cnt[y])){
sum1=dis[0]^dis[y];
sum2=dis[x]^dis[y];
sum3=dis[x];
ans=min(ans,max(sum1,max(sum2,sum3))-min(sum1,min(sum2,sum3)));
}
else {
sum1=dis[0]^dis[y]^dis[x];
sum2=dis[y];
sum3=dis[x];
ans=min(ans,max(sum1,max(sum2,sum3))-min(sum1,min(sum2,sum3)));
}
}
}
return ans;
}
};