题面
题解(区间DP输出方案)
我们知道给定一个二叉树的中序遍历,无法确定一颗二叉树,所以就会有很多二叉树
状态表示:f[i] [j] 表示中序遍历是 w[i~j] 的的所有二叉树的得分的最大值
状态计算:f[l] [r] = max( f[l] [k-1] * f[k+1] [r] + w[k] ) ,即将 f[l] [r] 表示的二叉树集合按根节点分类(分为左根右),则根节点在 k 时的最大得分即为 f[i][k - 1] * f[k + 1][j] + w[k],则f[i][j]即为遍历 k 所取到的最大值。
我们在计算每个状态时,记录每个区间的最大值所对应的根节点编号,最后直接通过DFS输出最大加分二叉树的前序遍历即可
代码
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 35;
int n;
int w[N];
int f[N][N];
int rt[N][N]; //[i,j]区间的根节点
//前序遍历(根左右)
void dfs(int l, int r) {
if (l > r) return;
int root = rt[l][r];
cout << root << " ";
dfs(l, root - 1);
dfs(root + 1, r);
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> w[i];
for (int len = 1; len <= n; len++) {
for (int l = 1; l + len - 1 <= n; l++) {
int r = l + len - 1;
if (len == 1) {
f[l][r] = w[l];
rt[l][r] = l;
} else {
for (int k = l; k <= r; k++) {
int left = k == l ? 1 : f[l][k - 1];
int right = k == r ? 1 : f[k + 1][r];
int value = left * right + w[k];
//要输出字典序最小,从前向后遍历,遇到比它的大的才会更新(等于不更新)
if (value > f[l][r]) {
f[l][r] = value;
rt[l][r] = k;
}
}
}
}
}
cout << f[1][n] << endl;
dfs(1, n);
return 0;
}