题意:

给定一棵含 个节点的二叉树,其节点编号为 。二叉树的中序遍历为 。每个节点有一个分数

任一棵子树subtree(也包含tree本身)的加分计算方法为:
subtree的左子树的加分 subtree的右子树的加分+subtree 的根的分数。

若左右子树中某一子树为空,规定其分数为 1。
若左右子树都为空(即叶节点),则分数为叶节点本身的分数。

求分树最高的 tree。

解法:

对二叉树的概念和二叉树的中序遍历有一定了解的话,就很容易得到这样的性质:
二叉树的中序遍历可以不断递归划分成:(左子树)根节点(右子树)。
比如样例输出的那棵二叉树:

根节点为 ,则可以划分成

根节点为 ,则划分成

根节点为 ,则划分成

有了这个最关键的性质中序遍历不断递归划分成(左子树)根节点(右子树)这样的结构

那么这道题的解法也很自然的出来了:区间 dp

表示编号从 构成的二叉树的分数的最大值。
枚举根节点 即可更新

再考虑一下边界条件,就可以按题意写出 方程:

题目需要输出二叉树的前序遍历。
可以再开一个数组,用 表示编号从 构成的二叉树的根节点编号。
转移的时候记录一下就可以了。
最后 dfs 输出前序遍历。

Code:

#include <bits/stdc++.h>
using namespace std;
const int N = 35;
using uint = unsigned int;
uint dp[N][N], pre[N][N], a[N], n;
void dfs(int l, int r) {
    if(l > r) return;
    int k = pre[l][r];
    cout << k << " ";
    dfs(l, k - 1);
    dfs(k + 1, r);
}
int main() {
    for(int i = 0; i < N; i++)
        for(int j = 0; j < N; j++)
            dp[i][j] = 1;
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i];
    for(int i = 1; i <= n; i++) {
        dp[i][i] = a[i];
        pre[i][i] = i;
    }
    for(int len = 2; len <= n; len++) 
        for(int i = 1; i + len - 1 <= n; i++) {
            int j = i + len - 1;
            dp[i][j] = 0;
            for(int k = i; k <= j; k++) { 
                uint v = dp[i][k - 1] * dp[k + 1][j] + a[k];
                if(dp[i][j] < v) {
                    dp[i][j] = v;
                    pre[i][j] = k;
                }
            }
        }
    cout << dp[1][n] << endl;
    dfs(1, n);
    return 0;
}