【算法练习】LeetCode-2322. 从树中删除边的最小分数

这个题目有些意思,可以给我们一些启发,题目如下,

力扣

存在一棵无向连通树,树中有编号从 0 到 n - 1 的 n 个节点, 以及 n - 1 条边。

给你一个下标从 0 开始的整数数组 nums ,长度为 n ,其中 nums[i] 表示第 i 个节点的值。另给你一个二维整数数组 edges ,长度为 n - 1 ,其中 edges[i] = [ai, bi] 表示树中存在一条位于节点 ai 和 bi 之间的边。

删除树中两条 不同 的边以形成三个连通组件。对于一种删除边方案,定义如下步骤以计算其分数:

分别获取三个组件 每个 组件中所有节点值的异或值。
最大 异或值和 最小 异或值的 差值 就是这一种删除边方案的分数。
例如,三个组件的节点值分别是:[4,5,7]、[1,9] 和 [3,3,3] 。三个异或值分别是 4 ^ 5 ^ 7 = 6、1 ^ 9 = 8 和 3 ^ 3 ^ 3 = 3 。最大异或值是 8 ,最小异或值是 3 ,分数是 8 - 3 = 5 。
返回在给定树上执行任意删除边方案可能的 最小 分数。

示例 1:


输入:nums = [1,5,5,4,11], edges = [[0,1],[1,2],[1,3],[3,4]]
输出:9
解释:上图展示了一种删除边方案。
- 第 1 个组件的节点是 [1,3,4] ,值是 [5,4,11] 。异或值是 5 ^ 4 ^ 11 = 10 。
- 第 2 个组件的节点是 [0] ,值是 [1] 。异或值是 1 = 1 。
- 第 3 个组件的节点是 [2] ,值是 [5] 。异或值是 5 = 5 。
分数是最大异或值和最小异或值的差值,10 - 1 = 9 。
可以证明不存在分数比 9 小的删除边方案。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/minimum-score-after-removals-on-a-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

把一个树去掉2条边,求得到的3个部分的一个函数值,

一个思路:固定0号节点为根节点,然后枚举要去掉的2条边,这个时候,需要判断2条边的关系,是否存在直系关系,可以考虑记录高度(层次),再一层层的回溯,判断是不是直系,

这个方法会超时,代码如下,

struct node {
    int id;
    int h;
    node* pre;
 //   vector<node*> nts;//nexts
    int val;//store data
    node() {
        h = 10000;
    }
};

class Solution {
public:
    int getv(int a, int b, int c) {
        int mx = max(a, max(b, c));
        int mi = min(a, min(b, c));
        return mx - mi;
    }
    
    map<int, vector<int>> ve;
    int dfs(int id, vector<int>& nums, int ih, vector<node>& nds) {
        nds[id].h = ih;
        nds[id].id = id;
        vector<int>& con = ve[id];
        int t = nums[id];
        for (int i = 0; i < con.size(); i++) {
            int cid = con[i];
            if (nds[cid].h < ih) continue;
            nds[cid].pre = &nds[id];
            t ^= dfs(cid, nums, ih+1, nds);
        }
        
        nds[id].val = t;
        return t;
    }
    
    int minimumScore(vector<int>& nums, vector<vector<int>>& edges) {
        
        //init the edges by v
        for (auto& v: edges) {
            int v0 = v[0];
            int v1 = v[1];
            ve[v0].push_back(v1);
            ve[v1].push_back(v0);
        }
        //get score for each v as root
        int len = nums.size();
        
        vector<node> nds(len);
        
        dfs(0, nums, 0, nds);
        
//        vector<vector<node*>> lay;//layer
//        nds[0].id = 0;
//        lay.push_back({&nds[0]});
        
        int all = nds[0].val;
   //     int mx = all; all may be very little, it is wrong to use all as mx at beginning
        int mx = 1000000000;
        
        int k, t, vhi, vhj;
        for (int i = 1; i < len; i++) {
            for (int j = i+1; j < len; j++) {
                int hi = nds[i].h;
                int hj = nds[j].h;
                if (hi == hj) {
                    vhi = nds[i].val;
                    vhj = nds[j].val;
                    k = all ^ vhi ^ vhj;
                    
                } else if (hi < hj) {
                    int a = hj;
                    node* p = &nds[j];
                    while (p->h > nds[i].h) {
                        p = p->pre;
                    }
                    if (p->id == i) {
                        vhj = nds[j].val;
                        k = all ^ nds[i].val;
                        vhi = nds[i].val ^ vhj;
                    } else {
                        vhi = nds[i].val;
                        vhj = nds[j].val;
                        k = all ^ vhi ^ vhj;
                    }
                    
                } else if (hi > hj) {
                    int a = hi;
                    node* p = &nds[i];
                    while (p->h > nds[j].h) {
                        p = p->pre;
                    }
                    if (p->id == j) {
                        vhi = nds[i].val;
                        k = all ^ nds[j].val;
                        vhj = nds[j].val ^ vhi;
                    } else {
                        vhi = nds[i].val;
                        vhj = nds[j].val;
                        k = all ^ vhi ^ vhj;
                    }
                }
                t = getv(k, vhi, vhj);
                mx = min(mx, t);
            }
        }
        
        return mx;
        
    }
};

判断是否直系的时候会花些时间,这里可以考虑空间换时间,使用hash方法存储一下每个节点的后代节点,就可以快速查找判断了,代码如下:

struct node {
    int id;
    int h;
 //   node* pre;
 //   vector<node*> nts;//nexts
    int val;//store data
    node() {
        h = 10000;
    }
};


class Solution {
public:
    int getv(int a, int b, int c) {
        int mx = max(a, max(b, c));
        int mi = min(a, min(b, c));
        return mx - mi;
    }
    
    map<int, vector<int>> ve;
    
    int dfs(int id, vector<int>& nums, int ih, vector<node>& nds, vector<vector<int>>& kds, vector<vector<int>>& hash) {
        nds[id].h = ih;
        nds[id].id = id;
        vector<int>& con = ve[id];
        int t = nums[id];
        kds[id].push_back(id);
        for (int i = 0; i < con.size(); i++) {
            int cid = con[i];
            if (nds[cid].h < ih) continue;      
            t ^= dfs(cid, nums, ih+1, nds, kds, hash);
            for (auto& v : kds[cid]) {
                kds[id].push_back(v);
                hash[id][v] = 1;
            }
        }
        
        nds[id].val = t;
        return t;
    }
    
    int minimumScore(vector<int>& nums, vector<vector<int>>& edges) {       
        //init the edges by v
        for (auto& v: edges) {
            int v0 = v[0];
            int v1 = v[1];
            ve[v0].push_back(v1);
            ve[v1].push_back(v0);
        }
        //get score for each v as root
        int len = nums.size();
        
        vector<node> nds(len);
        
        vector<vector<int>> kids(len);
        vector<vector<int>> hash(len, vector<int>(len, 0));
        
        dfs(0, nums, 0, nds, kids, hash);
        
        int all = nds[0].val;
   //     int mx = all; all may be very little, it is wrong to use all as mx at beginning
        int mx = 1000000000;
        
        int k, t, vhi, vhj;
        for (int i = 1; i < len; i++) {
            for (int j = i+1; j < len; j++) {
                vhi = nds[i].val;
                vhj = nds[j].val;
                if (hash[i][j] == 1) {
                    k = all ^ vhi;
                    vhi = vhi ^ vhj;
                } else if (hash[j][i] == 1) {
                    k = all ^ vhj;
                    vhj = vhj ^ vhi;
                } else {
                    k = all ^ vhi ^ vhj;
                }
                
                t = getv(k, vhi, vhj);
                mx = min(mx, t);
            }
        }
        
        return mx;   
    }
};

其实还有更好的方法,考虑另外一个问题:从一个树结构中选择2个节点,判断这2个节点是不是直系关系。

我们通过树的前序遍历栈处理来查看,一个节点push和pop的操作过程中的其他节点,就是该节点的后代节点,通过这样的标记,我们就可以来判断。使用dfs遍历也可以这样标记,

可参考

力扣icon-default.png?t=M5H6https://leetcode.cn/problems/minimum-score-after-removals-on-a-tree/solution/dfs-shi-jian-chuo-chu-li-shu-shang-wen-t-x1kk/

该题的另外一个方法,很巧妙,把2条要删除的边结构,构造到一个树结构中,认定是直系关系,同时其中1条边作为根节点上的边,这样就只需要枚举另外一个边了,直接从后代节点中查找。

参考

力扣icon-default.png?t=M5H6https://leetcode.cn/problems/minimum-score-after-removals-on-a-tree/solution/by-newhar-imbx/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值