题目描述
设一个n个节点的二叉树tree的中序遍历为(1,2,3,…,n),其中数字1,2,3,…,n为节点编号。每个节点都有一个分数(均为正整数),记第ii个节点的分数为di,tree及它的每个子树都有一个加分,任一棵子树subtree(也包含tree本身)的加分计算方法如下:
subtree左子树的加分×subtree的右子树的加分+subtree的根的分数。
若某个子树为空,规定其加分为1,叶子的加分就是叶节点本身的分数。不考虑它的空子树。
试求一棵符合中序遍历为(1,2,3,…,n)且加分最高的二叉树treetree。要求输出;
(1)treetree的最高加分
(2)treetree的前序遍历
输入格式
第1行:1个整数n(n<30),为节点个数。
第2行:n个用空格隔开的整数,为每个节点的分数(分数 <100)。
输出格式
第1行:1个整数,为最高加分(Ans≤4,000,000,000)。
第2行:n个用空格隔开的整数,为该树的前序遍历。
输入输出样例
输入
5
5 7 1 2 10
输出
145
3 1 2 4 5
设
f
i
j
f_{ij}
fij为i到j的最大加分子树,那么我们可以枚举顶点k 有转移
f
i
j
=
f
i
k
−
1
∗
f
k
+
1
r
+
a
[
k
]
f_{ij} = f_{ik-1} * f_{k+1r} +a[k]
fij=fik−1∗fk+1r+a[k]
每次更新的时候我们记录枚举出的i-j的根,最后根据遍历顺序输出。
#include<bits/stdc++.h>
#define ll long long
#define MAXN 5000100
#define N 1001
#define INF 0x3f3f3f3f
#define gtc() getchar()
using namespace std;
template <class T>
inline void read(T &s){
s = 0; T w = 1, ch = gtc();
while(!isdigit(ch)){if(ch == '-') w = -1; ch = gtc();}
while(isdigit(ch)){s = s * 10 + ch - '0'; ch = gtc();}
s *= w;
}
template <class T>
inline void write(T x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) write(x/10);
putchar(x % 10 + '0');
}
int n;
ll a[50];
ll f[100][100];
ll root[100][100];
int dfs(int l, int r){
// puts("1");
// printf("%d %d\n", l, r);
if(f[l][r] > 0) return f[l][r];
if(l == r) return a[l];
if(r < l) return 1;
for(int i = l; i <= r; ++i){
int x = dfs(l, i-1) * dfs(i+1, r) + f[i][i];
if(x > f[l][r]) f[l][r] = x, root[l][r] = i;
}
return f[l][r];
}
void out(int l, int r){
if(r < l) return ;
if(l == r){
printf("%d ", l); return ;
}
printf("%d ", root[l][r]);
out(l, root[l][r]-1); out(root[l][r]+1, r);
}
int main()
{
memset(f, 0, sizeof(f));
read(n);
for(int i = 1; i <= n; ++i) read(a[i]), f[i][i] = a[i];
cout << dfs(1, n) << endl;
out(1, n);
return 0;
}