力扣370周赛 -- 第三题(树形DP)

简介: 力扣370周赛 -- 第三题(树形DP)

1.png


该题的方法,也有点背包的意思,如果一些不懂的朋友,可以从背包的角度去理解该树形DP

问题


题解主要在注释里


        //该题是背包问题+树形dp问题的结合版,在树上解决背包问题


        //背包问题就是选或不选当前物品


//本题求的是最大分数


        //先转成背包问题理解


        //从n个物品当中选出最大分数


        //再转成有限制版的


        //从n个物品当中选出最大分数,并且血量是健康的


        //再转成树形DP去理解该问题


     


        //树是健康就是,在任意一条树的路径下(到叶子节点的任意一条路径),能确保至少有一个物品不被选


        //从树上前n个物品当中选出一些物品,并且保证树是健康的


        //从树上前i个物品当中选出保证树是健康的前提下,能选出的不超过i个物品的最大分数


        //然后再去拓展这个定义


        //结合树形DP的经验


        //以当前u为根节点的子树,在保证树是健康的前提下,能选出的最大分数


        //那么就有了推导过程,从下往上推导,也就是从最小子树往上推导到最大子树最大分数


        //那么最好的做法就是利用递归的特性,回溯的时候进行推导


        //这题中我们直接找出最大分数,其实是比较难的,我们用初中的思想


        //正难则反,既然找最大分数(有个不选的)比较难,那么我们可以用


        //找个最小分数(选上的),那么就变得比较简单了


        //状态定义


        //首先我们找最小的,以树形dp为经验推导出


        //我们以u为根节点的子树,总和最小的分数(并且是保证健康的,在一条路径上最少也得选一个)


        //定义为min_val[u]


        //那么怎么算出最大分数呢,既然有min_val[u],那么就有,以当前u为根节点的子树节点总和sum_val[u]


        //那么相减sum_val[u] - min_val[u]就等于最大分数了


        //那么如何推导这两个定义的数组呢?


        //树形dp类型问题,最好首先用dfs,边回溯边推导数组

class Solution {
public:
    void dfs_sum_val(int u,int fa,vector<int>& val,vector<vector<int>>& g,vector<long long>& sum_val)
    {
         sum_val[u] = val[u];
         for(auto e:g[u])
           if(fa != e)//不要往上计算,我们是从下往上推导,并且可以保证不会无限递归
           {
              dfs_sum_val(e,u,val,g,sum_val);
              //回溯计算
              sum_val[u] += sum_val[e];
           }
        //这个dfs我们可以算作一个小题,就是计算出每个点为根节点的子树的总和
    }
    void dfs_min_val(int u,int fa,vector<int>& val,vector<vector<int>>& g,vector<long long>& min_val)
    {
          long long min_res = 0;
          min_val[u] = (long long)val[u];//只有一个节点的时候必选
          for(auto& e:g[u])
          {
             if(fa != e)
             {
                 dfs_min_val(e,u,val,g,min_val);
                 min_res += min_val[e];
             }
          }
          if(min_res) min_val[u] = min((long long)min_val[u],min_res);
          //怎么选最小的分数呢,当前根节点选,那么子节点可以不选,当前根节点不选,那么子节点都要选
          //从下往上推导
    }
    long long maximumScoreAfterOperations(vector<vector<int>>& edges, vector<int>& values) 
    {
         //该题是背包问题+树形dp问题的结合版,在树上解决背包问题
         //背包问题就是选或不选当前物品
         //本题求的是最大分数
         //先转成背包问题理解
         //从n个物品当中选出最大分数
         //再转成有限制版的
         //从n个物品当中选出最大分数,并且血量是健康的
         //再转成树形DP去理解该问题
         //树是健康就是,在任意一条树的路径下(到叶子节点的任意一条路径),能确保至少有一个物品不被选
         //从树上前n个物品当中选出一些物品,并且保证树是健康的
         //从树上前i个物品当中选出保证树是健康的前提下,能选出的不超过i个物品的最大分数
         //然后再去拓展这个定义
         //结合树形DP的经验
         //以当前u为根节点的子树,在保证树是健康的前提下,能选出的最大分数
         //那么就有了推导过程,从下往上推导,也就是从最小子树往上推导到最大子树最大分数
         //那么最好的做法就是利用递归的特性,回溯的时候进行推导
         //这题中我们直接找出最大分数,其实是比较难的,我们用初中的思想
         //正难则反,既然找最大分数(有个不选的)比较难,那么我们可以用
         //找个最小分数(选上的),那么就变得比较简单了
         //状态定义
         //首先我们找最小的,以树形dp为经验推导出
         //我们以u为根节点的子树,总和最小的分数(并且是保证健康的,在一条路径上最少也得选一个)
         //定义为min_val[u]
         //那么怎么算出最大分数呢,既然有min_val[u],那么就有,以当前u为根节点的子树节点总和sum_val[u]
         //那么相减sum_val[u] - min_val[u]就等于最大分数了
         //那么如何推导这两个定义的数组呢?
         //树形dp类型问题,最好首先用dfs,边回溯边推导数组
         int edge_size = edges.size();
         vector<vector<int>> g(values.size() + 110);
         for(int i = 0;i < edge_size;i++)
         {
             int a = edges[i][0];
             int b = edges[i][1];
             g[a].push_back(b);
             g[b].push_back(a);
         }
         vector<long long> sum_val(21000);
         vector<long long> min_val(21000,0x3f3f3f3f);
         //预处理出来sum_val数组
         dfs_sum_val(0,-1,values,g,sum_val);
         //预处理出来min_val数组
         dfs_min_val(0,-1,values,g,min_val);
         return sum_val[0] - min_val[0];
    }
};
相关文章
|
3月前
|
Python
【Leetcode刷题Python】剑指 Offer 26. 树的子结构
这篇文章提供了解决LeetCode上"剑指Offer 26. 树的子结构"问题的Python代码实现和解析,判断一棵树B是否是另一棵树A的子结构。
52 4
|
3月前
|
Python
【Leetcode刷题Python】538. 把二叉搜索树转换为累加树
LeetCode上538号问题"把二叉搜索树转换为累加树"的Python实现,使用反向中序遍历并记录节点值之和来更新每个节点的新值。
22 3
|
6月前
|
算法 C语言 容器
从C语言到C++_25(树的十道OJ题)力扣:606+102+107+236+426+105+106+144+94+145(下)
从C语言到C++_25(树的十道OJ题)力扣:606+102+107+236+426+105+106+144+94+145
66 7
|
6月前
|
C语言
从C语言到C++_25(树的十道OJ题)力扣:606+102+107+236+426+105+106+144+94+145(中)
从C语言到C++_25(树的十道OJ题)力扣:606+102+107+236+426+105+106+144+94+145
57 1
|
6月前
|
算法 C语言 C++
从C语言到C++_25(树的十道OJ题)力扣:606+102+107+236+426+105+106+144+94+145(上)
从C语言到C++_25(树的十道OJ题)力扣:606+102+107+236+426+105+106+144+94+145
43 1
|
6月前
LeetCode———100——相同的树
LeetCode———100——相同的树
|
6月前
力扣337.打家劫舍3(树形dp)
力扣337.打家劫舍3(树形dp)
|
5月前
|
SQL 算法 数据可视化
LeetCode题目99:图解中叙遍历、Morris遍历实现恢复二叉树搜索树【python】
LeetCode题目99:图解中叙遍历、Morris遍历实现恢复二叉树搜索树【python】
|
5月前
|
存储 SQL 算法
LeetCode题目100:递归、迭代、dfs使用栈多种算法图解相同的树
LeetCode题目100:递归、迭代、dfs使用栈多种算法图解相同的树
|
5月前
|
存储 算法 数据可视化
python多种算法对比图解实现 验证二叉树搜索树【力扣98】
python多种算法对比图解实现 验证二叉树搜索树【力扣98】