设一个 n n n 个节点的二叉树 tree \text{tree} tree 的中序遍历为 ( 1 , 2 , 3 , … , n ) (1,2,3,\ldots,n) (1,2,3,…,n),其中数字 1 , 2 , 3 , … , n 1,2,3,\ldots,n 1,2,3,…,n 为节点编号。每个节点都有一个分数(均为正整数),记第 i i i 个节点的分数为 d i d_i di, tree \text{tree} tree 及它的每个子树都有一个加分,任一棵子树 subtree \text{subtree} subtree(也包含 tree \text{tree} tree 本身)的加分计算方法如下:
subtree \text{subtree} subtree 的左子树的加分 × subtree \times \text{subtree} ×subtree 的右子树的加分 + subtree + \text{subtree} +subtree的根的分数。
若某个子树为空,规定其加分为 1 1 1,叶子的加分就是叶节点本身的分数。不考虑它的空子树。
试求一棵符合中序遍历为 ( 1 , 2 , 3 , … , n ) (1,2,3,\ldots,n) (1,2,3,…,n) 且加分最高的二叉树 tree \text{tree} tree。要求输出
tree \text{tree} tree 的最高加分。
tree \text{tree} tree 的前序遍历。
第 1 1 1 行 1 1 1 个整数 n n n,为节点个数。
第 2 2 2 行 n n n 个用空格隔开的整数,为每个节点的分数
第 1 1 1 行 1 1 1 个整数,为最高加分( A n s ≤ 4 , 000 , 000 , 000 Ans \le 4,000,000,000 Ans≤4,000,000,000)。
第 2 2 2 行 n n n 个用空格隔开的整数,为该树的前序遍历。
n < 30 n< 30 n<30。
分数 < 100 <100 <100。
因为中序遍历为 ( 1 , 2 , 3 , … , n ) (1,2,3,\ldots,n) (1,2,3,…,n),所以我们可以得到每一棵子树都满足:左子树各节点序号<根<右子树各节点序号 所以想到区间DP
我们设 d p [ i ] [ j ] dp[i][j] dp[i][j]表示从 i i i ~ j j j生成树的最大加分
len呗,区间DP基本操作
枚举根节点 k ( l < k < r ) k(l <k<r) k(l<k<r), d p [ l ] [ r ] = m i n ( d p [ l ] [ r ] , d p [ l ] [ k − 1 ] ∗ d p [ k + 1 ] [ r ] + a [ k ] ) dp[l][r]=min(dp[l][r],dp[l][k-1]*dp[k+1][r]+a[k]) dp[l][r]=min(dp[l][r],dp[l][k−1]∗dp[k+1][r]+a[k])
for(int len=1;len<n;++len){ for(int l=1;l+len<=n;l++){ int r=l+len; dp[l][r]=dp[l+1][r]+a[l]; root[l][r]=l; for(int k=l+1;k<r;++k){ if(dp[l][r]<dp[l][k-1]*dp[k+1][r]+a[k]){ dp[l][r]=dp[l][k-1]*dp[k+1][r]+a[k]; root[l][r]=k; } } } }每次转移的时候保存一下根节点,有了根节点不就好做了吗\ (^o^)/~
void print(int l,int r){ if(l>r){ return; } printf("%d ",root[l][r]); if(l==r){ return; } print(l,root[l][r]-1); print(root[l][r]+1,r); }