加分二叉树
加分二叉树
时间限制: 1 Sec 内存限制: 512 MB
题目描述
设一个n个节点的二叉树tree的中序遍历为(1,2,3,...,n),其中数字1,2,3,...,n为节点编号。每个节点都有一个分数(均为正整数),记第i个节点的分数为di,tree及它的每个子树都有一个加分,任一棵子树subtree(也包含tree本身)的加分计算方法如下:
记subtree的左子树加分为l,右子树加分为r,subtree的根的分数为a,则subtree的加分为:l*r+a
若某个子树为空,规定其加分为1,叶子的加分就是叶节点本身的分数。不考虑它的空子树。
试求一棵符合中序遍历为(1,2,3,...,n)且加分最高的二叉树tree。
要求输出:
1.tree的最高加分;
2.tree的前序遍历。
记subtree的左子树加分为l,右子树加分为r,subtree的根的分数为a,则subtree的加分为:l*r+a
若某个子树为空,规定其加分为1,叶子的加分就是叶节点本身的分数。不考虑它的空子树。
试求一棵符合中序遍历为(1,2,3,...,n)且加分最高的二叉树tree。
要求输出:
1.tree的最高加分;
2.tree的前序遍历。
输入
第一行一个整数n表示节点个数;
第二行n个空格隔开的整数,表示各节点的分数。
第二行n个空格隔开的整数,表示各节点的分数。
输出
第一行一个整数,为最高加分b;
第二行n个用空格隔开的整数,为该树的前序遍历。
第二行n个用空格隔开的整数,为该树的前序遍历。
样例输入
5
5 7 1 2 10
样例输出
145
3 1 2 4 5
提示
对于100%的数据,n<30,b<100,结果不超过4e9。
我们老师把这道题放到了树形dp里,然而我用树形dp套路肝了两个多小时都没搞出来,然而有一个同学说这是区间dp,
我才恍然大悟,然后用不到1h就写完AC了
1.tree的最高加分:中序遍历有个特点就是假设节点x为根,则对于一个节点y,如果y>x则y在x的右子树上,如果x>y则
y在x的左子树上,这样我们对于每个序列都能很轻松地找到它的最优的根与这最优的值,最后输出最优的值即可
2.tree的前序遍历:这个比起第一个就简单多了,直接一个递归找子节点就好了
代码如下:
#include<bits/stdc++.h> using namespace std; int n; int v[31]; int dp[31][31]; int root[31][31]; int dfs(int l,int r) { if(dp[l][r]!=-1) { return dp[l][r]; } if(l==r) { return v[l]; } if(r<l) { return 1; } for(int i=l;i<=r;i++) { int p=dfs(l,i-1)*dfs(i+1,r)+dp[i][i]; if(p>dp[l][r]) { dp[l][r]=p; root[l][r]=i; } } return dp[l][r]; } void print(int l,int r) { if(r<l) { return; } if(l==r) { printf("%d ",l); return; } printf("%d ",root[l][r]); print(l,root[l][r]-1); print(root[l][r]+1,r); } int main() { memset(dp,-1,sizeof(dp)); scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&v[i]); dp[i][i]=v[i]; } printf("%d\n",dfs(1,n)); print(1,n); return 0; }