[区间DP]【NOIP2003T3】加分二叉树 题解

洛谷传送门在此

解题报告

一开始以为是树形DP,然后各种推理,就发现各种MLE。后来无耻的查了题解。再推一下,MD,这是个区间DP啊。

由于中序序列刚好是1…n,所以想到从里面找一个为根,然后就有左右两子树,然后又是对这两子树又分别找根。容易想到区间DP

定义 f[i][j]ij 的树的最大权值。 g[i][j] 表示最大值时的根。
转移方程如下:

f[i][j]=max(f[i][k1]f[k+1][j]+a[k])

注意初值。

复杂度:
时间: O(n3)
空间: O(n2)

#include<cstdio>
#include<cstring>
using namespace std;
int n,a[35],f[35][35],g[35][35];
inline char nc(){
    static char buf[100000],*pa=buf,*pb=buf;
    return pa==pb&&(pb=(pa=buf)+fread(buf,1,100000,stdin),pa==pb)?EOF:*pa++;
}
inline void readi(int &x){
    x=0; char ch=nc();
    while ('0'>ch||ch>'9') ch=nc();
    while ('0'<=ch&&ch<='9') {x=x*10+ch-'0'; ch=nc();}
}
void _dfs(int x,int y){
    if (x<=y){
        printf("%d ",g[x][y]);
        _dfs(x,g[x][y]-1);
        _dfs(g[x][y]+1,y);
    }
}
int main()
{
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    readi(n);
    for (int i=0;i<=n;i++)
        for (int j=0;j<=n;j++) f[i][j]=1;
    for (int i=1;i<=n;i++){
        readi(a[i]); g[i][i]=i; f[i][i]=a[i];
    }
    for (int L=2;L<=n;L++)
        for (int i=1,j;i<=n-L+1;i++){
            j=i+L-1;
            for (int k=i;k<=j;k++)
                if (f[i][j]<f[i][k-1]*f[k+1][j]+a[k]){
                    g[i][j]=k;
                    f[i][j]=f[i][k-1]*f[k+1][j]+a[k];
                }
        }
    printf("%d\n",f[1][n]); _dfs(1,n);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/try__jhf/article/details/78444360