[NOIP2003 提高组] 加分二叉树

news2024/11/24 20:24:30

[NOIP2003 提高组] 加分二叉树

题目描述:

设一个 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 行 1 个整数 n,为节点个数。

第 2 行 n 个用空格隔开的整数,为每个节点的分数

输出格式:

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

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

输入输出样例:

输入 #1:

5
5 7 1 2 10

输出 #1:

145
3 1 2 4 5

说明/提示:

数据规模与约定:

对于全部的测试点,保证 1≤n<30,节点的分数是小于 100 的正整数,答案不超过 4×10^9。

思路:

  如果你啥树的知识也不会,没关系,先跟我默念一遍:

   树 ,是递归定义的

此话怎讲?我们来看这个题的二叉树的积分规则

树的加分=左子树的加分× 的右子树的加分+根的分数

也就是说:树的加分,是由“左子树的加分、右子树的加分、根的分数”这三部分来决定的,那每一部分的加分又是怎么算的呢?

其中根的分数拿来用就行,而剩下两个,比如左子树的加分,不难发现左子树其实也是一棵树,它的积分规则也是由这三部分决定的

于是,我们又可以按着左子树的左子树和右子树算,就这样一环套一环,这有没有让你联想到什么呢?

没错,就是递归

那么递归总要有个头,不能一直下去没完了。那到什么时候才算结束呢?很简单,到叶节点的时候就结束啦!(不知道什么是叶节点的小伙伴注意啦:叶节点就是左右子树都空的节点,就像一棵树的叶子不会再往上长分枝了),递归到叶节点,直接返回该节点的加分,不用往下算啦!

到目前为止,怎么递归已经都想好了,那到底用什么算法,再具体点?

对啦,就是万能的深度优先搜索dfs!.


终于想好了思路,接下来终于要编代码啦!但是这是好多小伙伴肯定又犯了愁。正因为树这种数据结构的特殊性,我们应该怎么存储它呢???难道还需要开一个结构体吗?

悄悄告诉你,只需要一个数组就可以啦!

int n,a[40];//a来存储这棵树
cin>>n;
for(int i=1;i<=n;i++)
	cin>>a[i];

没错,我没骗你,就这么简单!

那可能各种问题又来了:我这么存储,怎样才能方便地获取每棵子树的信息呢?

别慌别慌,我们再看一下题目:输入的是这棵树的中序遍历,也就是左子树→根节点→右子树的遍历,我们可以设一个区间l~r,表示这棵树是a[l]到a[r]的部分,来看它是哪棵子树

for example,这棵树的中序遍历是:

5 7 1 2 10

那么1~5就觉得是整棵树,咱们假设中间的1为根节点,那根据中序遍历的顺序,根节点在中间,1左边的部分(5和7)也就是1~2,是左子树,右边的部分(2和10)也就是4~5,是右子树

也就是说,当整棵树为l~r,根结点为i时,左子树为l~i-1,右子树为i-1~r

懂了吧,那么再来几个!2~2是哪?

叶节点7!

那么5~4呢?

这个是一个空节点,并且,它的加分是1(题目都写了)

综上所述,我们总结到了三种情况:

  1. l<r时,1~r是指a[l]到a[r]的子树加分为左子树的加分× 的右子树的加分+根的分数
  2. l==r时,l~r是指叶节点a[l](或a[r],反正l==r嘛),加分为a[l]的值
  3. l>r时,l~r为空节点加分为1

于是我们就可以按这三条原则写出代码啦!在代码里,我们让每一个节点都当一次根节点,看看谁的最大

long long dfs(int l,int r){  //dfs函数,数据比较大,开long long保险 
	if(l>r)return 1;    //特殊情况1:如果为空节点,返回1 
	if(l==r)return a[l];   //特殊情况2:如果为叶节点,直接返回该节点的加分
   long long maxn=0;  //maxn来记录最大加分,作为最后的返回值
	for(int i=l;i<=r;i++){ 
		long long t=dfs(l,i-1)*dfs(i+1,r)+a[i];//t为以i为根节点的最大的加分 
		if(maxn<t) //更新最大值
			maxn=t;
    }
	return maxn;//返回最大值 
}

这样我们求最大加分的任务完成啦!但这还不够,这代码要递归这么多次,计算机这么可爱,怎么可以如此虐待计算机!重点是还会TLE,怎么办呢?

我们发现,我们在执行递归的过程中,同一棵子树算了一次又一次,会有很多重复的计算,何不把这些结果存起来,下次再用的时候直接拿过来就成! 好的,我们把代码改一下,由于加分是由l和r两个数决定的,我们就开一个名为dp的二维数组存吧!这次的代码,我们用dp[l][r]来记录l~r子树的最大加分,取代了maxn的位置

long long dp[40][40]={0};//dp[l][r]记录l~r子树的最大加分
long long dfs(int l,int r){   
	if(l>r)return 1;    
	if(l==r){           
		root[l][r]=l;
		return a[l];
	}
	if(dp[l][r])return dp[l][r];//特殊情况3:如果以前已经算过了,那就直接返回以前存起来的结果
	for(int i=l;i<=r;i++){ 
		long long t=dfs(l,i-1)*dfs(i+1,r)+a[i];
		if(dp[l][r]<t)
			dp[l][r]=t;
	}
	return dp[l][r];//返回最大值 
}

刚才的“如果以前已经算过了,那就直接返回以前存起来的结果”的思想,就是传说中的“记忆化搜索”,可以减少许多不必要的计算,再也不用担心我会TLE啦!


接下来的任务,就是输出最大加分的树的先序遍历。先序遍历的顺序是根节点→左子树→右子树,咱们还是用递归解决,定义一个名为root的二维数组,用来存储使l~r子树的加分最大的那个根节点,dfs咱都会编了,这种事那简直是a piece of cake!(小菜一碟)

void print(int l,int r){     //输出这棵树的先序遍历 
	if(l>r)return;      //如果节点为空(依然是l<r)结束 
	cout<<root[l][r]<<" ";  //先序遍历,先输出根节点 
	print(l,root[l][r]-1);  //然后左子树 
	print(root[l][r]+1,r);  //最后右子树 
}

注意题目让你输出的是节点的编号,也就是循环中的i而不是a[i],先把dfs函数里面加入储存root节点的代码,然后在print函数中按照先序遍历的顺序输出就完美解决了)


最后的最后,就是完整代码了!主程序应该很好编了吧!这里给大家介绍另一种处理叶节点的方法:由于它在dp数组和root数组中的值已经确定了,可以将它在主程序的循环中直接初始化最大加分和根结点位置。

完整代码:

#include<iostream> 
using namespace std;
int n,a[40],root[40][40];//a来存储中序遍历,root来存储最大积分的根节点 
long long dp[40][40]={0};//dp[l][r]记录从l区域到r区域最大的加分 
long long dfs(int l,int r){  //电风扇函数 
	if(l>r)return 1;    
	if(l==r){           //如果为叶节点,最大积分的根节点就是当前节点l 
		root[l][r]=l;
		return a[l];
	}
	if(dp[l][r])return dp[l][r];
	for(int i=l;i<=r;i++){ 
		long long t=dfs(l,i-1)*dfs(i+1,r)+a[i];
		if(dp[l][r]<t){ //更新最大值以及根节点位置 
			dp[l][r]=t;
			root[l][r]=i;
		}
	}
	return dp[l][r];
}
void print(int l,int r){     //输出这棵树的先序遍历 
	if(l>r)return;      //如果节点为空(依然是l<r)结束 
	cout<<root[l][r]<<" ";  //先序遍历,先输出根节点 
	print(l,root[l][r]-1);  //然后左子树 
	print(root[l][r]+1,r);  //最后右子树 
}
int main(){
	cin>>n;//输入
	for(int i=1;i<=n;i++){
		cin>>a[i];
		dp[i][i]=a[i];//叶节点的最大积分就是当前节点的积分,最大积分的根节点也是当前节点,直接给它初始化 
		root[i][i]=i;
	}
	cout<<dfs(1,n)<<endl;//从头到尾搜索 
	print(1,n);
	return 0; //树,是递归定义的 
}

 总结:

如果该题你认真思考的话,熟知树的结构,那是有很大几率可以做出来的。

祝大家早日AC!

The End~

 题目链接:

[NOIP2003 提高组] 加分二叉树 - 洛谷https://www.luogu.com.cn/problem/P1040

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/624325.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

优雅草蜻蜓T系统·专业版服务端以及后台部署说明-完整步骤-语音会议室支持多人语音,屏幕分享,导航配置,会议管理,会员管理

蜻蜓T系统专业版服务端以及后台部署 1&#xff0c;解压文件和基础环境配置 将源码用git工具克隆到/www/wwwroot git clone git地址 或者是由优雅草发送的商业源码文件包直接进行解压 ​ 编辑切换为居中 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09;…

十分钟柔和护理,轻松舒缓眼疲劳,云康宝眼部按摩仪体验

平时工作、生活中&#xff0c;每天都要长时间对着手机、电脑等电子设备&#xff0c;像是被它们吸走了灵魂&#xff0c;有时候眼睛干干的、痛痛的&#xff0c;像是被沙子刮过&#xff0c;光靠眼药水之类的东西根本解决不了问题&#xff0c;所以趁着618我入手了一款眼部按摩仪&am…

数字系统。网络层。IPv4 子网划分。ICMP

嘿&#xff0c;伙计们&#xff01;我希望你们一切都好。作为我每周更新计算机网络的一部分&#xff0c;我想分享一些令人兴奋的话题。 首先&#xff0c;我们深入研究了数字系统的世界。本主题重点介绍二进制和IPv4地址&#xff0c;我们学习了如何将二进制转换为十进制&#xf…

Zookeeper部署

Zookeeper的安装 环境变量的配置 上传安装包 使用MobaXterm、FinalShell或者使用scp将安装包apache-zookeeper-3.6.3-bin.tar.gz上传到/root/softwares下 复制代码 解压安装 [rootqianfeng01 ~]# tar -zxvf apache-zookeeper-3.6.3-bin.tar.gz -C /usr/local 复制代码 更名 …

1091 Acute Stroke (PAT甲级)

这道题用dfs做的话&#xff0c;因为递归太多层&#xff0c;堆栈溢出&#xff0c;有两个测试点过不了&#xff1b;所以用bfs。 但令我百思不得其解的是&#xff0c;我没用方向变量x[6], y[6], z[6]&#xff0c;直接老老实实算每一个方向的话&#xff0c;最后一个测试点过不了&a…

17.6:迪瑞克斯啦算法

迪瑞克斯啦算法 这个算法研究的是&#xff1a;有向的&#xff0c;没有负权重&#xff0c;可以有环的图。 这个算法主要研究的是&#xff1a;给出的节点到这张图的其他节点的最短路径是多少。用一个表表示出来。 思路&#xff1a; 如下图所示&#xff0c;我们想要求出a节点到其…

建立时间、保持时间和亚稳态

目录 一、建立时间和保持时间 二、亚稳态 三、避免亚稳态策略 四、多级寄存器阻断亚稳态传播 一、建立时间和保持时间 如图1所示&#xff0c;建立时间&#xff08;set up time&#xff09;是指在触发器的时钟信号上升沿到来以前&#xff0c;数据稳定不变的时间&#xff0c;…

【Apache Pinot】探究 Pinot 中存储模型的设计逻辑和 Segment 详解

背景 上一篇文章中&#xff0c;笔者简单介绍了一下分布式数据库 Pinot 的核心组件&#xff0c;本文主要针对其中的存储模型会做部分讲解。 如果你对读写磁盘有不错的基础的话&#xff0c;看起来会更轻松一些&#xff0c;如果没有也没关系&#xff0c;我会简单讲解一下这么设计…

使用STM32进行串口实验(非中断+中断)

关于串口相关的基本知识可以看这篇文章https://blog.csdn.net/weixin_62599865/article/details/129963991?spm1001.2014.3001.5501 一.使用非中断的方式进行串口通信 串口发送/接收函数&#xff1a; HAL_UART_Transmit(); 串口发送数据&#xff0c;使用超时管理机制 HAL_…

2023最新版本Activiti7系列-Activiti7概述和入门案例

一、Activiti7概述 官网地址&#xff1a;https://www.activiti.org/ Activiti由Alfresco软件开发&#xff0c;目前最高版本Activiti 7。是BPMN的一个基于java的软件实现&#xff0c;不过Activiti 不仅仅包括BPMN&#xff0c;还有DMN决策表和CMMN Case管理引擎&#xff0c;并且有…

【前端 - HTML】第 1 课 - HTML 初体验

欢迎来到博主 Apeiron 的博客&#xff0c;祝您旅程愉快 。 时止则止&#xff0c;时行则行。动静不失其时&#xff0c;其道光明。 目录 1、缘起 2、HTML 概念 2.1、HTML 定义 2.2、标签语法 3、HTML 基本骨架 4、标签的关系 5、注释 6、总结 1、缘起 最近在学习微信小程…

Apache Doris 冷热分层技术如何实现存储成本降低 70%?

在数据分析的实际场景中&#xff0c;冷热数据往往面临着不同的查询频次及响应速度要求。例如在电商订单场景中&#xff0c;用户经常访问近 6 个月的订单&#xff0c;时间较久远的订单访问次数非常少&#xff1b;在行为分析场景中&#xff0c;需支持近期流量数据的高频查询且时效…

C++ 使用一维数组和二维数组给 std::vector<cv::Point2d> 赋值的方法

文章目录 1. 一维数组给 vector 赋值的方法2. 一维 Point2d 数组给 vector<cv::Point2d> 赋值3. 二维 double 数组给 vector<cv::Point2d> 赋值 1. 一维数组给 vector 赋值的方法 &#xff08;1&#xff09;最简单的赋值方法是for循环遍历赋值&#xff0c;此处略过…

Python展开嵌套列表的五种方法

一、问题的提出 微信群中有人问&#xff0c;如何把以下内容转换成一个列表&#xff1a; 转换后&#xff1a; "[["007674","工银产业升级股票A","GYCYSJGPA","1.3574"],["007675","工银产业升级股票C",&qu…

d2l学习_第二章预备知识

x.1 Data Manipulation 数据操作。在Pytorch中常用的数据操作如下&#xff1a; 对于张量&#xff0c;即torch.Tensor类型的数据&#xff0c;你的任何操作都要把他想象成一个指针&#xff0c;因为等于运算符ab&#xff0c;会将b的张量内存地址赋值给a。 torch.Tensor类型的基…

day02-JavaScript-Vue

1 JavaScript html完成了架子&#xff0c;css做了美化&#xff0c;但是网页是死的&#xff0c;我们需要给他注入灵魂&#xff0c;所以接下来我们需要学习JavaScript&#xff0c;这门语言会让我们的页面能够和用户进行交互。 1.5.1.3 JSON对象 自定义对象 在 JavaScript 中自…

linux(信号发送后)

目录&#xff1a; 1.引入什么是合适的时候 2.内核态和用户态 3.信号的处理 4.sigaction函数 -------------------------------------------------------------------------------------------------------------------------------- 1.引入什么是合适的时候 2.信号什么时候被处…

你真的会PPT配色吗?来看看这篇吧,瞬间让你的PPT高大上起来

本文档使用技巧如下截图 在色彩里使用其它填充颜色 选取这个“吸管” 用于吸别人的颜色 我曾经为了出一个“惊艳”的PPT,光吸管用了不下150次。 好的艺术家复制,伟大的艺术家偷窃!--毕加索 下面就给出几大常用配色 各位在使用时注意看这些“色卡”的规律,那就是反差色…

安卓系统浏览器开发

预置某个浏览器为系统默认的浏览器 描述: 当系统存在多个浏览器时&#xff0c;如何预置某个浏览器为系统默认的浏览器&#xff1f; 方法: 1.在PackageManagerService.java中的构造函数结尾添加&#xff1a;setDefaultBrowser(); 2.setDefaultBrowser()的具体实现&#xff…

TDengine 合作伙伴 +1,这次是「DaoCloud道客」

随着我国数字经济持续快速发展&#xff0c;各行各业都在积极拥抱云技术&#xff0c;上云成为企业加快数字化转型步伐的关键一步。在此过程中&#xff0c;越来越多的企业开始意识到云原生技术的重要性&#xff0c;利用云原生更快地开发和部署应用程序&#xff0c;提高应用程序的…