题目告诉了我们中序遍历为 \((1,2,...,n)\),所以每一棵子树的根都会将这个区间一分为三。那么设 \(dp[l][r]\) 为将 \((l,l+1,l+2,...,r)\) 是中序遍历的子树的加分。那么转移的时候枚举一下根,也就是断点。每次转移的时候保存下根,方案递归输出。
\[dp[l,r]=\max_{l≤k≤r} \{dp[l,k-1]*dp[k+1,r]+A_k \}\]
#include <iostream>
#include <cstdio>
const int max_n = 30 + 5;
int N;
int Dp[max_n][max_n], Root[max_n][max_n];
inline int read()
{
register int x = 0;
register char ch = getchar();
while(!isdigit(ch)) ch = getchar();
while(isdigit(ch))
{
x = (x << 1) + (x << 3) + ch - ‘0‘;
ch = getchar();
}
return x;
}
void out(int l, int r)
{
printf("%d ", Root[l][r]);
if(l == r) return;
else if(Root[l][r] == l) out(Root[l][r] + 1, r);
else if(Root[l][r] == r) out(l, Root[l][r] - 1);
else
{
out(l, Root[l][r] - 1);
out(Root[l][r] + 1, r);
}
}
int main()
{
N = read();
for(int i = 1; i <= N; ++i)
{
Dp[i][i] = read();
Root[i][i] = i;
}
for(int len = 2; len <= N; ++len)
{
for(int l = 1; l <= N - len + 1; ++l)
{
int r = l + len - 1;
for(int k = l; k <= r; ++k)
{
int t = ((Dp[l][k - 1]) ? Dp[l][k - 1] : 1) * ((Dp[k + 1][r]) ? Dp[k + 1][r] : 1) + Dp[k][k];
if(Dp[l][r] < t)
{
Dp[l][r] = t;
Root[l][r] = k;
}
}
}
}
printf("%d\n", Dp[1][N]);
out(1, N);
return 0;
}