计蒜客奇怪的二叉树

问题描述

蒜头君最近遇到一道难题,想请聪明的你来帮忙解决一下。题目给了一棵奇怪的二叉树,树上有 n 个结点,每个结点按中序遍历的顺序依次编号为 1 到 n。每个结点都有一个权值,第 i 个结点的权值为 w  i ​  。每棵子树也有一个权值,记 s(i) 为以编号 i 为根结点的子树的权值,结点 i 的左孩子结点编号为 i  left,右孩子结点编号为 i  right,则 s(i) 的计算方法为:s(i)=s(i  left)×s(i  right)+w  i 。需要注意的是,如果结点i 为叶子结点,则s(i)=w  i ​  。如果结点 i 不是叶子结点,但是其左子树为空,则 s(i  left)=1;同样,如果结点 i 的右子树为空,则 s(i  right ​)=1。现在需要你来设计一棵二叉树,使得中序遍历得到的结点序列为 1 到 n,且使二叉树的根结点 root 的s(root) 最大。

输入格式 

输入有两行。输入第一行是一个整数 n(1≤n≤30),表示二叉树上一共有 n个结点。  输入第二行是 n个整数,每两个整数用一个空格隔开,第 i 个整数对应第 i个结点的权值 w i(1≤w i ​≤100)。

输出格式

输出两行。第一行输出一个整数,表示二叉树根结点 root 能获得的最大s(root),结果保证在 int 范围内。第二行输出 n 个整数,每两个整数之间用一个空格隔开。输出二叉树的前序遍历结果。

思路:

中序遍历中,root在中间,左右子树分别在两边。用dp[i][j]表示从i到j的中序遍历表示的子树长度,可从中枚举root,于是dp[i][j]=max(dp[i][j],dp[i][k-1]*dp[k+1][j]+s[k]),转化成石子合并问题。

关于输出,可以记录每一个区间的root节点,然后递归着输出。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=35;
int n,a[N],dp[N][N],root[N][N];
void printf_way(int l,int r){
	if(l>r) return ;
	printf("%d ",root[l][r]);
	printf_way(l,root[l][r]-1); printf_way(root[l][r]+1,r);
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(int i=1;i<=n;i++) {
		dp[i][i]=a[i]; root[i][i]=i;
	}
	for(int l=2;l<=n;l++)
		for(int i=1,j;i<=n-l+1;i++){
			j=i+l-1;
			for(int k=i+1;k<j;k++)
				if(dp[i][k-1]*dp[k+1][j]+a[k]>dp[i][j]){
				dp[i][j]=dp[i][k-1]*dp[k+1][j]+a[k];
				root[i][j]=k;
				}
			if(dp[i+1][j]+a[i]>dp[i][j]){
				dp[i][j]=dp[i+1][j]+a[i];
				root[i][j]=i;
			}
			if(dp[i][j-1]+a[j]>dp[i][j]){
				dp[i][j]=dp[i][j-1]+a[j];
				root[i][j]=j;
			}
		}
	printf("%d\n",dp[1][n]);
	printf_way(1,n);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值