【SSL 1838】加分二叉树(树形DP)

题目大意

设一个 n n n 个节点的二叉树 t r e e tree tree 的中序遍历为 ( 1 , 2 , 3 , . . . , n ) (1,2,3,...,n) (1,2,3,...,n),其中数字 1 , 2 , 3 , . . . , n 1,2,3,...,n 1,2,3,...,n为节点编号。每个节点都有一个分数(均为正整数),记第 i i i 个节点的分数为 d i d_i di t r e e tree tree 及它的每个子树都有一个加分,任一棵子树 s u b t r e e subtree subtree(也包含 t r e e tree tree 本身)的加分计算方法如下:

s u b t r e e subtree subtree 的左子树的加分 ∗ * s u b t r e e subtree subtree 的右子树的加分 + + + s u b t r e e subtree subtree 的根的分数。

若某个子树为空,规定其加分为 1 1 1,叶子的加分就是叶节点本身的分数。不考虑它的空子树。

试求一棵符合中序遍历为 ( 1 , 2 , 3 , . . . , n ) (1,2,3,...,n) (1,2,3,...,n) 且加分最高的二叉树 。要求输出
1. t r e e 1.tree 1.tree 的最高加分。
2. t r e e 2.tree 2.tree 的前序遍历。

对于全部的测试点,保证 1 ≤ n < 30 1≤n<30 1n<30,节点分数是小于 100 100 100的正整数。

输入格式
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≤4,000,000,000) (Ans4,000,000,000)
2 2 2 n n n 个用空格隔开的整数,为该树的前序遍历。

输入样例

5
5 7 1 2 10

输出样例

145
3 1 2 4 5

基本思路

这题说实话不算特别难,题目只给了中序遍历,中序遍历是什么?左 根 右。

这么说我们就 D F S DFS DFS,每一层枚举它的根,如此一来这个区间左边就是它的左子树,右边就是右子树,我们看看那个作为根答案最大就行了。

为了防止超时,我们还要小小地记忆化一下,这也算是 D P DP DP的一个点。

别忘了,题目还要输出前序遍历,所以我们要开一个 r o o t root root数组来存某个区间答案最大是根是什么。

核心代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=35;
ll n,a[N],root[N][N],dis[N][N];
//root[i][j]表示i~j区间值最大情况的根 
//dis[i][j]表示i~j区间的最大值(记忆化) 
//中序遍历:左 根 右
//注意,一个根可以,没有左子树或右子树,所以一个区间
//任何地方都可能是根 
inline ll dfs(int z,int y){
	if(z>y) return 1;//一个子树为空,没有这个子树
	if(z==y){//叶节点
		dis[z][y]=a[z];
		root[z][y]=z;
		return a[z];
	}
	if(dis[z][y]) return dis[z][y];//这个区间计算过了 
	for(int i=z;i<=y;i++){//枚举根 
		ll tmp=dfs(z,i-1)*dfs(i+1,y)+a[i];
		if(tmp>dis[z][y]){
			dis[z][y]=tmp;
			root[z][y]=i;
		}
	}
	return dis[z][y];
}
inline void print(int z,int y){//前序遍历输出
	if(z>y) return; 
	cout<<root[z][y]<<" ";
	print(z,root[z][y]-1);
	print(root[z][y]+1,y);
}
int main(){
	ios::sync_with_stdio(false);
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	cout<<dfs(1,n)<<endl;
	print(1,n);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值