【NOIP】加分二叉树

【NOIP】加分二叉树

题目

题目描述

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

subtree的左子树的加分× subtree的右子树的加分+subtree的根的分数。

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

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

(1)tree的最高加分

(2)tree的前序遍历

输入格式

第1行:1个整数n(n<30),为节点个数。

第2行:n个用空格隔开的整数,为每个节点的分数(分数 <100)。

输出格式

第1行:1个整数,为最高加分(0≤4,000,000,000)。

第2行:n个用空格隔开的整数,为该树的前序遍历。

输入输出样例

输入 #1 复制
5
5 7 1 2 10
输出 #1 复制
145
3 1 2 4 5

分析

首先,这里请允许我灵魂画图一波。。。。。。。(这个图是随便画的,不用看仔细。。。。。)
在这里插入图片描述
题目: 这里其实我们可以看成是,根 + 左子树最大值 * 右子树最大值 。(应该是废话。。。。。。。)

我们可以从左到右,让每个数都是根,进行一次dfs 求 左子树最大值 * 右子树最大值 + 根值

for(int i=L;i<R;i++){
		long long t = dfs(L,i-1) * dfs(i+1,R) + a[i];

然后每次max 比下(如果这次的数值更大就保存这次的数值 和根节点)根节点用数值root【】【】存放,

还要一个dp【】【】,每次 dfs结束,就把这次的dp【】【】结果存入,那么下次再遇到就直接放回这个数值,这是非常重要的
否则会超时

这里有一个问题,就是我们会dfs时遇到 dfs(L,R) ,然后L > R 。这样无疑是dfs无结果的,那就返回1;(题目要求:若某个子树为空,规定其加分为1)。

最后,题目还要输出这个树的中序。。。上面我们已经得到跟节点,那就用一个递归输出中序。。。。(这里过程不用说了把)

代码

#include<iostream>
#include<cstdio>
using namespace std;

int n,a[40],root[40][40];
long long dp[40][40];

long long dfs(int L,int R){
	if(L>R) return 1;       //因为要左右子节点相乘,所以如果是空则给1 
	
	if(dp[L][R]) return dp[L][R];  //关键一步,记忆化,以前走过那就直接返回结果 
	
	long long maxn = 0;
	for(int i=L;i<R;i++){
		long long t = dfs(L,i-1) * dfs(i+1,R) + a[i];
		if(t > maxn){
			maxn = t;
			root[L][R] = i;
		}
	}
	return dp[L][R] = maxn;	
}	


void dg(int L,int R){
	if(L>R) return ;            //防止出现 L >R 
	printf("%d ",root[L][R]);
	dg(L,root[L][R]-1);
	dg(root[L][R]+1,R);
}

int main(){
	
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		dp[i][i] = a[i];
		root[i][i] = i;
	}
	printf("%lld\n",dfs(1,n));
	dg(1,n);
	
	return 0;
} 
发布了75 篇原创文章 · 获赞 1 · 访问量 3648

猜你喜欢

转载自blog.csdn.net/A793488316/article/details/104623814
今日推荐