题目大意
设一个 n n n 个节点的二叉树 t r e e tree tree 的中序遍历为 ( 1 , 2 , 3 , . . . , n ) (1,2,3,...,n) (1,2,3,...,n),其中数字 1 , 2 , 3 , . . . , n 1,2,3,...,n 1,2,3,...,n为节点编号。每个节点都有一个分数(均为正整数),记第 i i i 个节点的分数为 d i d_i di, t r e e tree tree 及它的每个子树都有一个加分,任一棵子树 s u b t r e e subtree subtree(也包含 t r e e tree tree 本身)的加分计算方法如下:
s u b t r e e subtree subtree 的左子树的加分 ∗ * ∗ s u b t r e e subtree subtree 的右子树的加分 + + + s u b t r e e subtree subtree 的根的分数。
若某个子树为空,规定其加分为 1 1 1,叶子的加分就是叶节点本身的分数。不考虑它的空子树。
试求一棵符合中序遍历为
(
1
,
2
,
3
,
.
.
.
,
n
)
(1,2,3,...,n)
(1,2,3,...,n) 且加分最高的二叉树 。要求输出
1.
t
r
e
e
1.tree
1.tree 的最高加分。
2.
t
r
e
e
2.tree
2.tree 的前序遍历。
对于全部的测试点,保证 1 ≤ n < 30 1≤n<30 1≤n<30,节点分数是小于 100 100 100的正整数。
输入格式
第
1
1
1 行
1
1
1 个整数
n
n
n,为节点个数。
第
2
2
2 行
n
n
n 个用空格隔开的整数,为每个节点的分数。
输出格式
第
1
1
1 行
1
1
1 个整数,为最高加分
(
A
n
s
≤
4
,
000
,
000
,
000
)
(Ans≤4,000,000,000)
(Ans≤4,000,000,000)。
第
2
2
2 行
n
n
n 个用空格隔开的整数,为该树的前序遍历。
输入样例
5
5 7 1 2 10
输出样例
145
3 1 2 4 5
基本思路
这题说实话不算特别难,题目只给了中序遍历,中序遍历是什么?左 根 右。
这么说我们就 D F S DFS DFS,每一层枚举它的根,如此一来这个区间左边就是它的左子树,右边就是右子树,我们看看那个作为根答案最大就行了。
为了防止超时,我们还要小小地记忆化一下,这也算是 D P DP DP的一个点。
别忘了,题目还要输出前序遍历,所以我们要开一个 r o o t root root数组来存某个区间答案最大是根是什么。
核心代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=35;
ll n,a[N],root[N][N],dis[N][N];
//root[i][j]表示i~j区间值最大情况的根
//dis[i][j]表示i~j区间的最大值(记忆化)
//中序遍历:左 根 右
//注意,一个根可以,没有左子树或右子树,所以一个区间
//任何地方都可能是根
inline ll dfs(int z,int y){
if(z>y) return 1;//一个子树为空,没有这个子树
if(z==y){//叶节点
dis[z][y]=a[z];
root[z][y]=z;
return a[z];
}
if(dis[z][y]) return dis[z][y];//这个区间计算过了
for(int i=z;i<=y;i++){//枚举根
ll tmp=dfs(z,i-1)*dfs(i+1,y)+a[i];
if(tmp>dis[z][y]){
dis[z][y]=tmp;
root[z][y]=i;
}
}
return dis[z][y];
}
inline void print(int z,int y){//前序遍历输出
if(z>y) return;
cout<<root[z][y]<<" ";
print(z,root[z][y]-1);
print(root[z][y]+1,y);
}
int main(){
ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
cout<<dfs(1,n)<<endl;
print(1,n);
return 0;
}