当前位置:首页 > 牛客 > 【牛客234249题解析】评分树问题:动态规划与递归遍历的解题思路与代码实现

【牛客234249题解析】评分树问题:动态规划与递归遍历的解题思路与代码实现

15小时前

【牛客234249题解析】评分树问题:动态规划与递归遍历的解题思路与代码实现 牛客题解 动态规划 区间DP 递归遍历 二叉树 前序遍历 状态转移方程 第1张

一、题目解读

牛客234249题要求构建一棵评分,通过给定的分数数组计算最大加分路径,并返回路径节点及总分。题目核心在于如何将区间动态规划树结构遍历结合,寻找最优解。理解题目需把握“评分规则”与“树形结构”的关系,即通过节点分割区间,计算子区间乘积与得分之和的最大值。

二、解题思路

采用动态规划(Dynamic Programming)与递归遍历的策略。首先通过区间DP计算任意区间内的最大得分及对应根节点,再利用前序遍历构建树结构。关键在于:

1. 定义二维DP数组存储区间最大得分和根节点索引。

2. 初始化单个节点为自身得分,逐步扩展区间长度求解。

3. 递归函数实现前序遍历,记录根节点路径。

三、解题步骤

1. 初始化:创建dp(得分矩阵)、root(根节点矩阵)、result(最终结果容器)。单个节点得分即原始分数,根节点为自己。

2. 区间DP计算:

    外层循环控制区间长度(len=2~n),内层循环遍历起始位置。

    对每个区间,枚举分割点k,计算左右子区间的乘积与当前得分,更新最大值及对应根节点。

3. 前序遍历构建路径:

    定义递归函数preorder(l, r),通过root[l][r]确定当前区间的根节点,递归左子树和右子树。

    将根节点加入traversal列表,最终形成前序遍历序列。

4. 结果构造:将总分存入result[0],路径存入result[1]并返回。

四、代码与注释

class Solution {
public:
    vector<vector<int>> scoreTree(vector<int>& scores) {
        int n = scores.size();
        vector<vector<int>> dp(n+2, vector<int>(n+2, 0)); // 区间最大得分
        vector<vector<int>> root(n+2, vector<int>(n+2, 0)); // 区间根节点
        vector<vector<int>> result(2); // 最终结果:总分与路径
        
        // 初始化单个节点情况
        for(int i = 1; i <= n; i++) {
            dp[i][i] = scores[i-1]; // 得分即原始分数
            root[i][i] = i; // 根节点为自己
        }
        
        // 区间DP计算最大加分
        for(int len = 2; len <= n; len++) { // 区间长度
            for(int i = 1; i <= n-len+1; i++) { // 起始位置
                int j = i + len - 1; // 结束位置
                for(int k = i; k <= j; k++) { // 枚举分割点
                    int left = (k == i)? 1 : dp[i][k-1]; // 左子区间得分
                    int right = (k == j)? 1 : dp[k+1][j]; // 右子区间得分
                    int current = left * right + scores[k-1]; // 当前得分(乘积+节点分)
                    
                    if(current > dp[i][j]) { // 更新最大值
                        dp[i][j] = current;
                        root[i][j] = k; // 记录根节点
                    }
                }
            }
        }
        
        // 前序遍历
        vector<int> traversal;
        function<void(int, int)> preorder = [&](int l, int r) {
            if(l > r) return; // 递归终止条件
            traversal.push_back(root[l][r]); // 记录根节点
            preorder(l, root[l][r]-1); // 遍历左子树
            preorder(root[l][r]+1, r); // 遍历右子树
        };
        preorder(1, n); // 从整个区间开始遍历
        
        // 构造返回结果
        result[0].push_back(dp[1][n]); // 总分
        result[1] = traversal; // 路径节点
        return result;
    }
};

注释说明:代码中已标注关键逻辑,包括初始化、DP状态转移方程、递归遍历的实现细节,帮助读者理解算法核心。

五、总结

该解法巧妙运用区间DP解决最优子结构问题,通过记录根节点避免重复计算,递归遍历高效构建树路径。在算法竞赛或实际应用中,动态规划与树结构结合的思路具有广泛适用性。需注意边界处理(如单节点特例)和递归终止条件,确保正确性与效率。此外,代码结构清晰,注释明确,可作为类似问题的参考模板。


原创内容 转载请注明出处

分享给朋友:

相关文章

力扣501题最优解:不用额外空间找出BST中的众数?这个解法让你大开眼界

力扣501题最优解:不用额外空间找出BST中的众数?这个解法让你大开眼界

题目解读‌:在二叉搜索树的世界里,每个节点都默默记录着自己的数值。现在我们需要找出这些数值中出现频率最高的那些数字,也就是所谓的"众数"。有趣的是,二叉搜索树本身具有左小右大的特性...

LeetCode 120题三角形最小路径和最优解法:动态规划详解与代码实现

LeetCode 120题三角形最小路径和最优解法:动态规划详解与代码实现

一、题目解读LeetCode 120题“三角形最小路径和”要求给定一个由数字组成的三角形,从顶部开始向下移动,每次可向左或向右移动一格,计算从顶至底的最小路径和。三角形以二维向量形式给出,每层元素数量...

CSP-J 2019公交换乘题解析:基于队列优化的动态规划代码详解

CSP-J 2019公交换乘题解析:基于队列优化的动态规划代码详解

一、题目解读CSP-J 2019年的“公交换乘”题目(洛谷P5661)要求模拟地铁与公交交替出行的费用计算。题目核心在于地铁消费会产生优惠券,而公交可在45分钟内使用优惠券抵扣车费。需要处理n条出行记...

力扣931题最小下降路径和解析 动态规划解法 LeetCode解题技巧

力扣931题最小下降路径和解析 动态规划解法 LeetCode解题技巧

一、题目解读力扣931题「Minimum Falling Path Sum」(最小下降路径和)要求在一个n x n的整数矩阵中,计算从顶部到底部的最小路径和。路径只能从每个位置向下或对角线移动(即向下...

牛客网288555题解题指南:动态规划求解小红的暑假(附代码解析)

牛客网288555题解题指南:动态规划求解小红的暑假(附代码解析)

一、题目解读牛客网288555题要求解决一个组合数学问题:有三位朋友,每天需邀请其中一位参加聚会,但不能连续两天邀请同一位朋友。给定天数n,求满足条件的不同邀请方案总数。题目考察动态规划、状态转移及组...

洛谷P10472题解:利用栈求解最长有效括号

洛谷P10472题解:利用栈求解最长有效括号

一、题目解读洛谷P10472题要求计算给定字符串中最长有效括号的长度。有效括号指括号成对匹配(如"()[]{}"),子串需连续且内部嵌套正确。题目核心在于判断括号匹配的连续性,并找...

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。