加分二叉树
题意:
给你一个数的中序表达式,然后一颗子树的分数=左子树的分数*右子树的分数+根的分数
给你所有点的分数,让你构造出分值最大的树,输出前序遍历
题解:
区间dp问题
设dp[i][j]表示中序遍历是w[i~j]的所有二叉树的得分的最大值
树张什么样都可以,而我们只是想要分数最高
所以我们从小的子树开始,先决定区间长度,然后枚举区间的起点,决定区间的终点,我们求出每一小区间的最大分值,求大的区间时就用小的合并上
区间[l,r]的最大分数
k在[l,r]中,为枚举的根
dp[i][j]=max(dp[i][k-1]*dp[k+1][j]+w[k]),保留最大值的同时记录根,方便之后的前序遍历输出
代码:
#include<bits/stdc++.h>
#define debug(a,b) printf("%s = %d\n",a,b);
typedef long long ll;
using namespace std;inline int read(){int s=0,w=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();//s=(s<<3)+(s<<1)+(ch^48);return s*w;
}
const int maxn=60;
int w[maxn];
int f[maxn][maxn];
int root[maxn][maxn];
void dfs(int l,int r){if(l>r)return ;int k=root[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>>w[i];for(int len=1;len<=n;len++){for(int i=1;i+len-1<=n;i++){int j=i+len-1;for(int k=i;k<=j;k++){int right,left;if(k==i){left=1;right=f[k+1][j];}else if(k==j){left=f[i][k-1];right=1;}else {left=f[i][k-1];right=f[k+1][j];}int score=left*right+w[k];if(score>f[i][j]){f[i][j]=score;root[i][j]=k;}}}}cout<<f[1][n]<<endl;dfs(1,n);return 0;
}