给你一个正整数数组 arr,考虑所有满足以下条件的二叉树:
- 每个节点都有 0 个或是 2 个子节点。
- 数组
arr中的值与树的中序遍历中每个叶节点的值一一对应。(知识回顾:如果一个节点有 0 个子节点,那么该节点为叶节点。) - 每个非叶节点的值等于其左子树和右子树中叶节点的最大值的乘积。
在所有这样的二叉树中,返回每个非叶节点的值的最小可能总和。这个和的值是一个 32 位整数。
示例:
输入:arr = [6,2,4]
输出:32
解释:
有两种可能的树,第一种的非叶节点的总和为 36,第二种非叶节点的总和为 32。
24 24
/ \ / \
12 4 6 8
/ \ / \
6 2 2 4
题解 :
/**
* @param {number[]} arr
* @return {number}
*/
// 方法一:单调栈,极小值,归纳,贪心
// 这个题实际上是将数组中相邻的数两两合并,计算他们的乘积之和,求最小的乘积之和。
// 合并相邻的两个数之后得到的是较大的一个数。
// 例如:
// 对于[6, 2, 4, 8],假设乘积之和为 res = 0;
// 可以将 2 与 4 合并,变为[6, 4, 8],res = 0 + 8 = 8;
// 然后将 6 与 4 合并,变为[6, 8],res = 8 + 24 = 32;
// 最后将 6 与 8 合并,变为[8],res = 32 + 48 = 80。
// 第一轮:arr[i] = 6 stack = [6] res = 0;
// 第二轮:arr[i] = 2 stack = [6, 2] res = 0;
// 第三轮:arr[i] = 4 stack = [6, 4] 栈顶值小于当前值 2 与 4 合并 res = 8
// 第四轮:arr[i] = 8 stack = [6] 栈顶值小于当前值 4 与 6 合并 res = 24 + 8 = 32
// arr[i] = 8 stack = [8] 栈顶值小于当前值 6 与 8 合并 res = 48 + 32 = 80
// 例如:
// 对于[3, 2, 4],假设乘积之和为 res = 0;
// 可以将 3 与 2 合并,变为[3, 4],res = 0 + 6 = 6;
// 然后将 3 与 4 合并,变为[4],res = 6 + 12 = 18;
// 我们需要思考的就是如何合并,才能使得乘积之和 res 最小
var mctFromLeafValues = function (arr) {
const n = arr.length;
const stack = [];
let res = 0;
for (let i = 0; i < n; i++) {
while (stack.length && (stack[stack.length - 1] <= arr[i] || i === n - 1)) {
const bottom = stack.pop();
if (stack.length) {
res += Math.min(stack[stack.length - 1], arr[i]) * bottom;
} else {
res += bottom * arr[i];
}
// 单调递减时处理合并相邻两个数据之后得到最大的值
arr[i] = Math.max(bottom, arr[i]);
}
stack.push(arr[i]);
}
return res;
};
// 方法二:动态规划
// 定义状态:
// dp[i][j] 表示将 arr[i...j]合并之后所得的最小乘积之和。
// dp[0][len] 就是最终答案。
// 状态转移方程:
// dp[i][j] = Math.min(dp[i][j], dp[i][k] + dp[k + 1][j] + maxVal[i][k] * maxVal[k + 1][j])
// 这里的 maxVal[i][j] 代表从 i 到 j 的区间,最大的元素是 maxVal[i][j]。
// 注意:这里的 dp[i][j] 是最终的和,最终的最小乘积之和,需要将 dp[i][k] 与 dp[k + 1][j] 的和加上。
// 我们可以将 arr[i...j]分开,分为 arr[i...k]和 arr[k + 1...j],
// arr[i...k]和 arr[k + 1...j] 这两个区间最终可以合并为两个数,再将这两个数合并。
// 我们需要不断枚举 k,使得得到的 dp[i][j] 最小。
var mctFromLeafValues = function (arr) {
const n = arr.length;
const dp = new Array(n).fill(0).map(() => new Array(n).fill(0));
const maxVal = new Array(n).fill(0).map(() => new Array(n).fill(0));
for (let i = 0; i < n; i++) {
maxVal[i][i] = arr[i];
for (let j = i + 1; j < n; j++) {
maxVal[i][j] = Math.max(maxVal[i][j - 1], arr[j]);
}
}
for (let len = 1; len < n; len++) {
for (let i = 0; i + len < n; i++) {
dp[i][i + len] = Number.MAX_SAFE_INTEGER;
for (let k = i; k < i + len; k++) {
dp[i][i + len] = Math.min(
dp[i][i + len],
dp[i][k] + dp[k + 1][i + len] + maxVal[i][k] * maxVal[k + 1][i + len]);
}
}
}
return dp[0][n - 1];
};
来源:力扣(LeetCode)
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。