牛客234249题最优二叉树构建:区间DP解法详解与代码实现
一、问题背景
牛客234249题要求我们根据给定的节点分数数组,构建一棵加分最大的二叉树。每个节点的加分计算规则为:左子树加分 × 右子树加分 + 当前节点分数。本文将详细解析使用区间动态规划(DP)解决该问题的完整思路。
二、核心算法解析
三、完整代码实现(带详细注释)
class Solution { public: vector<vector<int>> scoreTree(vector<int>& scores) { int n = scores.size(); // dp[i][j]表示区间i到j的最大加分 vector<vector<int>> dp(n+2, vector<int>(n+2, 0)); // root[i][j]记录区间i到j的根节点位置 vector<vector<int>> root(n+2, vector<int>(n+2, 0)); // 结果数组:result[0]存储最大加分,result[1]存储前序遍历序列 vector<vector<int>> result(2); // 初始化单个节点情况(区间长度为1) for(int i = 1; i <= n; i++) { dp[i][i] = scores[i-1]; // 单个节点的加分就是自身分数 root[i][i] = i; // 单个节点的根就是自己 } // 区间DP计算最大加分(从区间长度2开始逐步扩大) for(int len = 2; len <= n; len++) { for(int i = 1; i <= n-len+1; i++) { int j = i + len - 1; // 尝试所有可能的分割点k for(int k = i; k <= j; k++) { // 左子树加分(k为左边界时设为1) int left = (k == i) ? 1 : dp[i][k-1]; // 右子树加分(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的填表顺序:必须按区间长度从小到大计算,确保子问题先被解决
边界处理:当k为区间端点时,空子树的加分设为1(乘法单位元)
时间复杂度:O(n³) 三重循环,空间复杂度O(n²)
重构树结构:利用root表通过前序遍历即可重建最优二叉树
原创内容 转载请注明出处