传送门:https://www.luogu.org/problem/show?pid=1040
题目描述
设一个n个节点的二叉树tree的中序遍历为(1,2,3,…,n),其中数字1,2,3,…,n为节点编号。每个节点都有一个分数(均为正整数),记第i个节点的分数为di,tree及它的每个子树都有一个加分,任一棵子树subtree(也包含tree本身)的加分计算方法如下:
subtree的左子树的加分× subtree的右子树的加分+subtree的根的分数。
若某个子树为空,规定其加分为1,叶子的加分就是叶节点本身的分数。不考虑它的空子树。
试求一棵符合中序遍历为(1,2,3,…,n)且加分最高的二叉树tree。要求输出;
(1)tree的最高加分
(2)tree的前序遍历
输入格式
第1行:一个整数n(n<30),为节点个数。
第2行:n个用空格隔开的整数,为每个节点的分数(分数<100)。
输出格式
第1行:一个整数,为最高加分(结果不会超过4,000,000,000)。
第2行:n个用空格隔开的整数,为该树的前序遍历。
样例君
输入
5
5 7 1 2 10
输出
145
3 1 2 4 5
蒟蒻吐槽
被机房dalao带着刷dp题,虽然蒟蒻更想悄咪咪地码一码线段树。
言归正传,蒟蒻乍一看还以为是树形dp,结果是一道区间dp,和其他dp不太一样的地方,这里要在k循环时特判一下以k为根的子树没有左右儿子的情况。其他的,就和普通区间dp没什么区别了。特别的,也就是转移上一层状态时中间要把决策点k的空间留出来方便加上根的贡献。上代码了,对于区间dp还没有什么了解的战友,蒟蒻建议看一下这个博客,快照传送门,普通传送门,因为蒟蒻在新疆,有的网站上不去,只有快照了,其他地方的战友应该可以上,所以有两个传送门。
那个tr[i][j]的二维数组,记录的是区间l到r的根。代码有点丑。
另,因为蒟蒻实在上不了csdn,如果侵权,希望原作者可以留言给我,谢谢。
代码
#include<cstdio> const int N=50; int n,tr[N][N]; long long dp[N][N]; void dfs(int l,int r) { if(l>r)return; printf("%d ",tr[l][r]); dfs(l,tr[l][r]-1); dfs(tr[l][r]+1,r); return; } int main() { scanf("%d",&n); for(int i=1;i<=n;++i) scanf("%lld",&dp[i][i]),tr[i][i]=i; for(int l=2;l<=n;++l) { for(int i=1;i<=n-l+1;++i) { int j=i+l-1; if(j>n)break; for(int k=i;k<=j;++k) { if(k==i&&dp[i][j]<dp[k+1][j]+dp[k][k])dp[i][j]=dp[k+1][j]+dp[k][k],tr[i][j]=k; else if(k==j&&dp[i][j]<dp[i][k-1]+dp[k][k])dp[i][j]=dp[i][k-1]+dp[k][k],tr[i][j]=k; else if(k!=i&&k!=j&&dp[i][j]<dp[i][k-1]*dp[k+1][j]+dp[k][k]) { dp[i][j]=dp[i][k-1]*dp[k+1][j]+dp[k][k]; tr[i][j]=k; } } } } printf("%lld\n",dp[1][n]); dfs(1,n); return 0; }