LeetCode2322:dfs序下的父子顺序判断、异或性质

-题目大意概述-

       给定一棵树,有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;
    }
};

       

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值