题意:
给一颗有 n个结点的树的中序遍历,让你算出一种构造方法使得该树的分数最大,且输出方案,分数的计算如下:
① 叶子结点的分数就是它本身的权值;
② 非叶子结点,若该结点有两颗子树,则为两颗子树的分数相乘加上该结点的权值,若该结点只有一颗子树,则为这颗子树的分数加上该结点的权值
最后整颗树的分数就是根节点的分数
思路:
可以知道若中序遍历是这样的 a1,a2,a3,a4,…,an,若这颗是以a3为根节点的树那么,a1 ~ a2就是它的左子树,a4 ~ an就是它的右子树。那么a3就是区间[1,n]的根或者说合并点,然后得到的分数就是a1 ~ a2 合并的分数加上a4 ~ an合并的分数,和合并石子的区间DP问题一样,但是要记录方案就是记录每一个合并点是哪个,因为输出前序遍历,只需要递归地输出就可以了先输出(1,n)的合并点k,然后输出(1,k-1)的合并点…
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 35;
int n,w[N],f[N][N],g[N][N];
void dfs(int l,int r){
if(l > r) return;
int root = g[l][r];
printf("%d ",root);
dfs(l,root-1);
dfs(root+1,r);
}
int main(){
scanf("%d",&n);
for(int i = 1;i <= n;i++) scanf("%d",w+i);
for(int len = 1;len <= n;len++){
for(int i = 1;i+len-1 <= n;i++){
int j = i+len-1;
if(len == 1){
f[i][j] = w[i];
g[i][j] = i;
}else{
//枚举以谁为根节点
for(int k = i;k <= i+len-1;k++){
int left = (k==i?1:f[i][k-1]);
int right = (k==j?1:f[k+1][j]);
int score = left*right + w[k];
if(f[i][j] < score){
f[i][j] = score;
g[i][j] = k;
}
}
}
}
}
printf("%d\n",f[1][n]);
dfs(1,n);
puts("");
return 0;
}