题目描述: 给出一个正整数数组arr
,考虑所有满足以下条件的二叉树:
每个节点都有 $0$ 个或 $2$ 个节点。 数组 arr
中的值与树的中序遍历中每个叶节点的值一一对应。 每个非叶节点的值等于其左子树和右子树中叶节点的最大值的乘积。 数据范围:
$2\le arr.len \le 40,1\le arr[i] \le 15$
题解: 动态规划: 可以发现,只要数组的长度大于等于 $2$ 就一定存在对应的二叉树。因此可以合并两个相邻的区间更新答案,可以使用区间dp。用区间dp,直接从区间长度为 $2$ 开始遍历,也没有问题。
区间dp,枚举区间 $len \in [2, n]$ ,枚举区间左端点 $i \in [0, n - len + 1)$ , $i$ 从 $0$ 到 $i + len - 1 \lt n$ ,这样的话得到区间右端点 $j = i + len - 1$ 。最后枚举区间断点 $k\in [i, j)$ 。因为是 $k + 1$ ,所以不能枚举到 $j$ 。
初始条件,因为区间长度为 $1$ 不符合条件,区间长度为 $2$ 时等于直接相乘,所以长度为 $1$ 的区间初始化为 $0$ 。
单调栈做法: 先合并的深度更大,后面被乘的次数更多。因此需要把小的先合并。选择极小值,然后看两侧哪个数字比较小,和更小的一个合并。使用单调减栈,维护递减序列。如果遇到一个大于栈顶的元素 $x$ ,就弹出栈顶元素 $y$ ,然后判断当前元素 $x$ 和当前栈顶元素 $z$ 的大小,如果 $x < z$ 则合并 $x$ 与 $y$ ,加上合并的代价,把 $x$ 入栈;否则合并 $y$ 与 $z$ ,把 $z$ 入栈,把 $x$ 入栈。
感觉有点类似哈夫曼编码。
代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 auto optimize_cpp_stdio = [](){ std ::ios::sync_with_stdio(false ); std ::cin .tie(nullptr ); std ::cout .tie(nullptr ); return 0 ; }(); class Solution { public : const static int maxn = 4e1 + 10 ; const static int maxm = 1e5 + 10 ; const int INF = 0x3f3f3f3f ; int dp[maxn][maxn]; int maxl[maxn][maxn]; int mctFromLeafValues (vector <int > &arr) { int n = arr.size(); for (int i = 0 ; i < n; ++i) for (int j = i; j < n; ++j) { if (i == j) dp[i][j] = 0 ; else dp[i][j] = INF; } for (int i = 0 ; i < n; ++i) { int maxx = arr[i]; for (int j = i; j < n; ++j) { maxx = max(maxx, arr[j]); maxl[i][j] = maxx; } } for (int len = 2 ; len <= n; ++len) { for (int i = 0 ; i + len - 1 < n; ++i) { int j = i + len - 1 ; for (int k = i; k < j; ++k) { dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1 ][j] + maxl[i][k] * maxl[k + 1 ][j]); } } } return dp[0 ][n - 1 ]; } };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 auto optimize_cpp_stdio = [](){ std ::ios::sync_with_stdio(false ); std ::cin .tie(nullptr ); std ::cout .tie(nullptr ); return 0 ; }(); class Solution { public : const static int maxn = 4e1 ; int st[maxn]; int top = -1 ; int mctFromLeafValues (vector <int > &arr) { int n = arr.size(); int ans = 0 ; for (int i = 0 ; i < n; ++i) { while (top >= 0 && st[top] <= arr[i]) { int y = st[top--]; if (top < 0 || st[top] > arr[i]) { ans += arr[i] * y; } else { ans += st[top] * y; } } st[++top] = arr[i]; } while (top >= 1 ) { int y = st[top--]; ans += y * st[top]; } return ans; } };