设一个n
个节点的二叉树tree的中序遍历为(l, 2, 3, …, n)
,其中数字 1, 2, 3, …, n
为节点编号。每个节点都有一个分数(均为正整数),记第j
个节点的分数为,tree及它的每个子树都有一个加分,任一棵子树subtree(也包含tree本身)的加分计算方法如下:
- subtree的左子树的加分× subtree的右子树的加分+subtree的根的分数
- 若某个子树为空,规定其加分为
1
,叶子的加分就是叶节点本身的分数。不考虑它的空子树。
试求一棵符合中序遍历为1, 2, 3, …, n)
且加分最高的二叉树tree。要求输出;
- tree的最高加分
- tree的前序遍历
我们注意到给出的数据是中序遍历,对于中序遍历, 在根的左右两侧,恰好是他的左右两棵子树的序列
我们只要枚举区间内的根节点即可
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> typedef long long ll; using namespace std; const int maxn = 50; int n, v[maxn], f[maxn][maxn], root[maxn][maxn]; //root[i][j]表示区间 i - j 内的根节点 //f[i][j] 表示 i - j 的最大加分 void print(int l, int r){//前序输出区间[l, r] if(l>r) return; if(l==r){ printf("%d ", l); return; } printf("%d ", root[l][r]); print(l, root[l][r]-1); print(root[l][r]+1, r); } int main(){ scanf("%d", &n); for(int i=1; i<=n; i++) scanf("%d", &v[i]);//读入分数 for(int i=1; i<=n; i++) f[i][i] = v[i],f[i][i-1] = 1, root[i][i]=i;//初始化,f[i][i-1]表示空子树 for(int len=1; len<n; len++){//枚举区间长度 for(int i=1; i+len<=n; i++){//枚举起点 i int j = i+len; //终点 j f[i][j] = f[i + 1][j] + f[i][i];//初始化假设左子树为空 root[i][j] = i; //从左子树为空开始 for(int k=i+1; k<j; k++){//枚举区间内根节点 if(f[i][j]<f[i][k-1]*f[k+1][j]+f[k][k]){ f[i][j] = f[i][k-1]*f[k+1][j]+f[k][k]; root[i][j] = k; } } } } printf("%d ", f[1][n]); print(1, n); return 0; }