题目描述
设一个 n n n 个节点的二叉树 tree \text{tree} tree 的中序遍历为 ( 1 , 2 , 3 , … , n ) (1,2,3,\ldots,n) (1,2,3,…,n),其中数字 1 , 2 , 3 , … , n 1,2,3,\ldots,n 1,2,3,…,n 为节点编号。每个节点都有一个分数(均为正整数),记第 i i i 个节点的分数为 d i d_i di, tree \text{tree} tree 及它的每个子树都有一个加分,任一棵子树 subtree \text{subtree} subtree(也包含 tree \text{tree} tree 本身)的加分计算方法如下:
subtree \text{subtree} subtree 的左子树的加分 × \times × subtree \text{subtree} subtree 的右子树的加分 + + + subtree \text{subtree} subtree 的根的分数。
若某个子树为空,规定其加分为 1 1 1,叶子的加分就是叶节点本身的分数。不考虑它的空子树。
试求一棵符合中序遍历为 ( 1 , 2 , 3 , … , n ) (1,2,3,\ldots,n) (1,2,3,…,n) 且加分最高的二叉树 tree \text{tree} tree。要求输出
-
tree \text{tree} tree 的最高加分。
-
tree \text{tree} tree 的前序遍历。
输入格式
第 1 1 1 行 1 1 1 个整数 n n n,为节点个数。
第 2 2 2 行 n n n 个用空格隔开的整数,为每个节点的分数
输出格式
第 1 1 1 行 1 1 1 个整数,为最高加分($ Ans \le 4,000,000,000$)。
第 2 2 2 行 n n n 个用空格隔开的整数,为该树的前序遍历。
样例
样例输入1:
5
5 7 1 2 10
样例输出1:
145
3 1 2 4 5
数据范围
对于全部的测试点,保证 1 ≤ n < 30 1 \le n< 30 1≤n<30,节点的分数是小于 100 100 100 的正整数,答案不超过 4 × 1 0 9 4 \times 10^9 4×109。
题解
观察到 n ≤ 30 n \le 30 n≤30,求最高加分,因此考虑到 DP。
设 f i , j f_{i, j} fi,j 表示 i i i 到 j j j 的最大加分。
转移时枚举中转点 k k k,把 k k k 当作根,计算左右子树的加分的乘积和该节点的加分之和,即 f i , j = f i , k − 1 × f k + 1 , j + a i f_{i, j} = f_{i, k - 1} \times f_{k + 1, j} + a_i fi,j=fi,k−1×fk+1,j+ai。注意左右子树为空的情况要特判。
如何求这棵树呢?
在转移时,如果能更新 f i , j f_{i, j} fi,j,就用一个 d d d 数组记录区间 [ i , j ] [i, j] [i,j] 的根(也可以记录 k − i k - i k−i)。输出方案时,进行 dfs。dfs 记录区间 [ l , r ] [l, r] [l,r],每次找出根 t t t 输出,然后继续递归 [ l , t − 1 ] [l, t - 1] [l,t−1] 和 [ t + 1 , r ] [t + 1, r] [t+1,r]。
#include<bits/stdc++.h>
using namespace std;
int n;
int a[40];
int f[40][40];
int d[40][40];
void dfs(int x, int y){//输出方案
if(x > y){
return;
}
printf("%d ", x + d[x][y]);
dfs(x, x + d[x][y] - 1);
dfs(x + d[x][y] + 1, y);
}
int main(){
memset(f, 0, sizeof(f));
memset(d, 0, sizeof(d));
scanf("%d", &n);
for(int i = 1; i <= n; ++ i){
scanf("%d", &a[i]);
}
for(int i = 1; i <= n; ++ i){//初始化
f[i][i] = a[i];
d[i][i] = 0;
}
for(int k = 2; k <= n; ++ k){
for(int i = 1; i <= n - k + 1; ++ i){
int j = i + k - 1, t1, t2;
for(int u = i; u <= j; ++ u){
if(u == i){
t1 = 1;
}
else{
t1 = f[i][u - 1];
}
if(u == j){
t2 = 1;
}
else{
t2 = f[u + 1][j];
}
int t = t1 * t2 + a[u];
if(f[i][j] < t){
f[i][j] = t;
d[i][j] = u - i;
}
}
}
}
printf("%d\n", f[1][n]);
dfs(1, n);
return 0;
}