解题报告
一开始以为是树形DP,然后各种推理,就发现各种MLE。后来无耻的查了题解。再推一下,MD,这是个区间DP啊。
由于中序序列刚好是1…n,所以想到从里面找一个为根,然后就有左右两子树,然后又是对这两子树又分别找根。容易想到区间DP
定义
f[i][j]为i…j
的树的最大权值。
g[i][j]
表示最大值时的根。
转移方程如下:
f[i][j]=max(f[i][k−1]∗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;
}