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

Problem

image

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;
}
posted @ 2025-04-26 10:27  一位XXS  阅读(37)  评论(0)    收藏  举报