设一个nn个节点的二叉树tree的中序遍历为(1,2,3,…,n1,2,3,…,n),其中数字1,2,3,…,n1,2,3,…,n为节点编号。每个节点都有一个分数(均为正整数),记第ii个节点的分数为di,treedi,tree及它的每个子树都有一个加分,任一棵子树subtreesubtree(也包含treetree本身)的加分计算方法如下:
subtreesubtree的左子树的加分× subtreesubtree的右子树的加分+subtreesubtree的根的分数。
若某个子树为空,规定其加分为11,叶子的加分就是叶节点本身的分数。不考虑它的空子树。
试求一棵符合中序遍历为(1,2,3,…,n1,2,3,…,n)且加分最高的二叉树treetree。要求输出;
(1)treetree的最高加分
(2)treetree的前序遍历
输入格式
第11行:11个整数n(n<30)n(n<30),为节点个数。
第22行:nn个用空格隔开的整数,为每个节点的分数(分数<100<100)。
输出格式
第11行:11个整数,为最高加分(Ans \le 4,000,000,000≤4,000,000,000)。
第22行:nn个用空格隔开的整数,为该树的前序遍历。
输入输出样例
输入 #1复制
5 5 7 1 2 10
输出 #1复制
145 3 1 2 4 5
思路:
- 中序遍历的树根左边是左子树,右边是右子树
- 在子树中序遍历序列中,依旧为一个树根,左边是左子树,右边是右子树
- 最大加分二叉树的子树也为最大加分二叉树
- 因此可将其视为区间DP,并得出递推式:dp[i][j]=max(dp[i][j],dp[i][m-1]*dp[m+1][j]+dp[m][m]); dp[i][j]表示序列i ~j可形成的最大加分二叉树,m为枚举的根序号,范围i~j
#include<iostream>
#include<algorithm>
using namespace std;
int n,mid[32][32]={0},dp[32][32];
void first(int x,int y){ //先序遍历区间X~Y,并输出序列
if(x==y){
cout<<" "<<x;
return;
}
if(y<x)return ;
cout<<" "<<mid[x][y];
first(x,mid[x][y]-1);
first(mid[x][y]+1,y);
}
int main() {
cin>>n;
for(int i=0; i<=n; i++)
fill(dp[i],dp[i]+n+2,1);
for(int i=1; i<=n; i++)
cin>>dp[i][i],mid[i][i]=i;
for(int k=1; k<n; k++) { //枚举区间大小 ,k表示间隔大小
for(int i=1,j=i+k; j<=n; i++,j++) { //枚举区间起始和终点
for(int m=i; m<=j; m++) { //枚举树根
int t=dp[i][m-1]*dp[m+1][j]+dp[m][m]; //递推式
if(dp[i][j]<t){
dp[i][j]=t;
mid[i][j]=m; //存储树根
}
}
}
}cout<<dp[1][n]<<endl; //区间i到n的最大加分二叉树大小
cout<<mid[1][n];//输出区间1~n的树根
first(1,mid[1][n]-1); //输出区间1~n-1的所有树根
first(mid[1][n]+1,n);
return 0;
}