加分二叉树
题解:
这道题需要用到中序遍历的一个性质。在中序遍历中,每一个节点都有可能是我们的根节点(last点),一旦我们的根节点确定了,我们根节点假设为i,那么i-1就是左子树,i+1就是右子树。所以按照步骤来。
1,集合:确定以根节点为i的一颗树的二叉树加分。
2,属性:最大值。
3,状态:区间DP一般是L和R。
4,last点:如上所说,我们会根据我们确定的根节点的不同导致我们最后的答案不同。
所以我们的状态转移式为:f[l][r]=max(f[l][i-1]*f[i+1][r]+w[i])。
然后题目要求我们按照字典序小的顺序输出我们的节点。
字典序问题,因为我们DP过程的时候我们就是从左到右按照序号从小到大的顺序进行DP的并且我们遇见以当前节点为根严格大于我们上一个节点为根的分数的时候才进行更新。所以这样的方案已经保证了我们的根节点是字典序小的。最后再dfs输出就好了。
#include<bits/stdc++.h>
using namespace std;
const int N=35;
int f[N][N],a[N],rt[N][N];
void dfs(int l, int r)
{
if (l > r) return;
int k = rt[l][r];
printf("%d ", k);
dfs(l, k - 1);
dfs(k + 1, r);
}
int main()
{
int n; cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int len=1;len<=n;len++){
for(int l=1;l+len-1<=n;l++){
int r=l+len-1;
for(int k=l;k<=r;k++){
if(len==1){
f[l][r]=a[l];
rt[l][r]=l;
}else{
for(int k=l;k<=r;k++){
int left=(k==l?1:f[l][k-1]);
int right=(r==k?1:f[k+1][r]);
int score=left*right+a[k];
if(score>f[l][r]){
f[l][r]=score;
rt[l][r]=k;
}
}
}
}
}
}
cout<<f[1][n]<<endl;
dfs(1,n);
puts("");
return 0;
}