• 【算法练习】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条边的关系,是否存在直系关系,可以考虑记录高度(层次),再一层层的回溯,判断是不是直系,

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

    1. struct node {
    2. int id;
    3. int h;
    4. node* pre;
    5. // vector<node*> nts;//nexts
    6. int val;//store data
    7. node() {
    8. h = 10000;
    9. }
    10. };
    11. class Solution {
    12. public:
    13. int getv(int a, int b, int c) {
    14. int mx = max(a, max(b, c));
    15. int mi = min(a, min(b, c));
    16. return mx - mi;
    17. }
    18. map<int, vector<int>> ve;
    19. int dfs(int id, vector<int>& nums, int ih, vector<node>& nds) {
    20. nds[id].h = ih;
    21. nds[id].id = id;
    22. vector<int>& con = ve[id];
    23. int t = nums[id];
    24. for (int i = 0; i < con.size(); i++) {
    25. int cid = con[i];
    26. if (nds[cid].h < ih) continue;
    27. nds[cid].pre = &nds[id];
    28. t ^= dfs(cid, nums, ih+1, nds);
    29. }
    30. nds[id].val = t;
    31. return t;
    32. }
    33. int minimumScore(vector<int>& nums, vector<vector<int>>& edges) {
    34. //init the edges by v
    35. for (auto& v: edges) {
    36. int v0 = v[0];
    37. int v1 = v[1];
    38. ve[v0].push_back(v1);
    39. ve[v1].push_back(v0);
    40. }
    41. //get score for each v as root
    42. int len = nums.size();
    43. vector<node> nds(len);
    44. dfs(0, nums, 0, nds);
    45. // vector<vector<node*>> lay;//layer
    46. // nds[0].id = 0;
    47. // lay.push_back({&nds[0]});
    48. int all = nds[0].val;
    49. // int mx = all; all may be very little, it is wrong to use all as mx at beginning
    50. int mx = 1000000000;
    51. int k, t, vhi, vhj;
    52. for (int i = 1; i < len; i++) {
    53. for (int j = i+1; j < len; j++) {
    54. int hi = nds[i].h;
    55. int hj = nds[j].h;
    56. if (hi == hj) {
    57. vhi = nds[i].val;
    58. vhj = nds[j].val;
    59. k = all ^ vhi ^ vhj;
    60. } else if (hi < hj) {
    61. int a = hj;
    62. node* p = &nds[j];
    63. while (p->h > nds[i].h) {
    64. p = p->pre;
    65. }
    66. if (p->id == i) {
    67. vhj = nds[j].val;
    68. k = all ^ nds[i].val;
    69. vhi = nds[i].val ^ vhj;
    70. } else {
    71. vhi = nds[i].val;
    72. vhj = nds[j].val;
    73. k = all ^ vhi ^ vhj;
    74. }
    75. } else if (hi > hj) {
    76. int a = hi;
    77. node* p = &nds[i];
    78. while (p->h > nds[j].h) {
    79. p = p->pre;
    80. }
    81. if (p->id == j) {
    82. vhi = nds[i].val;
    83. k = all ^ nds[j].val;
    84. vhj = nds[j].val ^ vhi;
    85. } else {
    86. vhi = nds[i].val;
    87. vhj = nds[j].val;
    88. k = all ^ vhi ^ vhj;
    89. }
    90. }
    91. t = getv(k, vhi, vhj);
    92. mx = min(mx, t);
    93. }
    94. }
    95. return mx;
    96. }
    97. };

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

    1. struct node {
    2. int id;
    3. int h;
    4. // node* pre;
    5. // vector<node*> nts;//nexts
    6. int val;//store data
    7. node() {
    8. h = 10000;
    9. }
    10. };
    11. class Solution {
    12. public:
    13. int getv(int a, int b, int c) {
    14. int mx = max(a, max(b, c));
    15. int mi = min(a, min(b, c));
    16. return mx - mi;
    17. }
    18. map<int, vector<int>> ve;
    19. int dfs(int id, vector<int>& nums, int ih, vector<node>& nds, vector<vector<int>>& kds, vector<vector<int>>& hash) {
    20. nds[id].h = ih;
    21. nds[id].id = id;
    22. vector<int>& con = ve[id];
    23. int t = nums[id];
    24. kds[id].push_back(id);
    25. for (int i = 0; i < con.size(); i++) {
    26. int cid = con[i];
    27. if (nds[cid].h < ih) continue;
    28. t ^= dfs(cid, nums, ih+1, nds, kds, hash);
    29. for (auto& v : kds[cid]) {
    30. kds[id].push_back(v);
    31. hash[id][v] = 1;
    32. }
    33. }
    34. nds[id].val = t;
    35. return t;
    36. }
    37. int minimumScore(vector<int>& nums, vector<vector<int>>& edges) {
    38. //init the edges by v
    39. for (auto& v: edges) {
    40. int v0 = v[0];
    41. int v1 = v[1];
    42. ve[v0].push_back(v1);
    43. ve[v1].push_back(v0);
    44. }
    45. //get score for each v as root
    46. int len = nums.size();
    47. vector<node> nds(len);
    48. vector<vector<int>> kids(len);
    49. vector<vector<int>> hash(len, vector<int>(len, 0));
    50. dfs(0, nums, 0, nds, kids, hash);
    51. int all = nds[0].val;
    52. // int mx = all; all may be very little, it is wrong to use all as mx at beginning
    53. int mx = 1000000000;
    54. int k, t, vhi, vhj;
    55. for (int i = 1; i < len; i++) {
    56. for (int j = i+1; j < len; j++) {
    57. vhi = nds[i].val;
    58. vhj = nds[j].val;
    59. if (hash[i][j] == 1) {
    60. k = all ^ vhi;
    61. vhi = vhi ^ vhj;
    62. } else if (hash[j][i] == 1) {
    63. k = all ^ vhj;
    64. vhj = vhj ^ vhi;
    65. } else {
    66. k = all ^ vhi ^ vhj;
    67. }
    68. t = getv(k, vhi, vhj);
    69. mx = min(mx, t);
    70. }
    71. }
    72. return mx;
    73. }
    74. };

    其实还有更好的方法,考虑另外一个问题:从一个树结构中选择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/

  • 相关阅读:
    vue3+ts+setup语法糖
    【MySQL】如何在Linux上安装MySQL
    【图】按公因数计算最大组件大小 并查集
    【Python3】【力扣题】231. 2 的幂
    最高检:聚焦金融、电信、互联网等领域 打击泄露个人信息违法犯罪
    闲人闲谈PS之三十四——项目成本费用控制阈值
    12 克莱姆法则的几何解释
    猿创征文|HCIE-Security Day53:Anti-DDoS设备简单谈
    STM32基于HAL库RT-Thread Demo测试
    【Android】浅记图片加载框架 -- Glide
  • 原文地址:https://blog.csdn.net/aaajj/article/details/125548122