P1040 [NOIP 2003 提高组] 加分二叉树
Problem

Solve
最大价值怎么求
中序遍历要求是1-n,看起来很烦,但是我们可以通过确定一个根并且拼接上左右子树组成
也就是说,中序遍历为\(1\sim x-1\)的子树和中序遍历为\(x+1\sim n\)的子树配合上编号为x的根即可组成中序遍历为1-n的树
由此我们发现一个数的状态取决于子树,且在编号上具有连贯性,故可以尝试区间DP
设f[x][y][z]为以z为根的中序遍历为x-y的数的最大价值
那么我们可以枚举两边的子节点得到答案
前序遍历怎么算
转移的时候搞一个l[x][y][z]和r[x][y][z],记录转移到f[x][y][z]的左子树和右子树的编号,再dfs即可
时间复杂度\(O(n^5),n\le 30\) 够用
Code
#include<bits/stdc++.h>
using namespace std;
long long f[35][35][35],l[35][35][35],r[35][35][35];
long long n,a[35],ans,root;
void dfs(int L,int R,int x){
if(!x||R<L)return ;
cout<<x<<" ";
dfs(L,x-1,l[L][R][x]);
dfs(x+1,R,r[L][R][x]);
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
f[i][i][i]=a[i];
}
for(int len=2;len<=n;len++){
for(int i=1;i<=n;i++){
int j=i+len-1;
if(j>n)break;
for(int k=i;k<=j;k++){
for(int k1=i;k1<k;k1++){
for(int k2=k+1;k2<=j;k2++){
if(f[i][k-1][k1]*f[k+1][j][k2]+a[k]>f[i][j][k]){
f[i][j][k]=f[i][k-1][k1]*f[k+1][j][k2]+a[k];
l[i][j][k]=k1;
r[i][j][k]=k2;
}
}
}
}
for(int k2=i+1;k2<=j;k2++){
if(f[i+1][j][k2]+a[i]>f[i][j][i]){
f[i][j][i]=f[i+1][j][k2]+a[i];
r[i][j][i]=k2;
}
}
for(int k1=i;k1<j;k1++){
if(f[i][j-1][k1]+a[j]>f[i][j][j]){
f[i][j][j]=f[i][j-1][k1]+a[j];
l[i][j][j]=k1;
}
}
}
}
for(int k=1;k<=n;k++){
if(f[1][n][k]>ans){
ans=f[1][n][k];
root=k;
}
}
cout<<ans<<endl;
dfs(1,n,root);
return 0;
}

浙公网安备 33010602011771号