NOIP2003提高组第二轮T3:加分二叉树

news2024/9/24 18:34:08

题目链接

[NOIP2003 提高组] 加分二叉树

题目描述

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

subtree \text{subtree} subtree 的左子树的加分 × \times × subtree \text{subtree} subtree 的右子树的加分 + + + subtree \text{subtree} subtree 的根的分数。

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

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

  1. tree \text{tree} tree 的最高加分。

  2. tree \text{tree} tree 的前序遍历。

输入格式

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 \le 4,000,000,000 Ans4,000,000,000)。

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

样例 #1

样例输入 #1

5
5 7 1 2 10

样例输出 #1

145
3 1 2 4 5

提示

数据规模与约定

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

算法思想

最高加分

根据题目描述:

  • 一棵二叉树的中序遍历为 ( 1 , 2 , 3 , … , n ) (1,2,3,\ldots,n) (1,2,3,,n)

在中序遍历中,一旦确定了根结点,那么左右子树的节点编号一定在根结点两侧,例如当根节点为 3 3 3时,那么左子树的结点编号为 1 , 2 1,2 1,2,右子树的结点编号为 4 , 5 , … , n 4,5,\ldots, n 4,5,,n,如下图所示:

在这里插入图片描述

  • 加分计算方法为左子树的加分 × \times × 右子树的加分 + + +根的分数。

求一棵符合中序遍历为 ( 1 , 2 , 3 , … , n ) (1,2,3,\ldots,n) (1,2,3,,n) 且加分最高的二叉树,就是求以根节点为中心,将左右两个区间(子树)合并在一起的最大值,因此可以使用区间型动态规划进行处理。

状态表示

f [ i , j ] f[i,j] f[i,j]表示二叉树中序遍历的节点编号在区间 [ i , j ] [i,j] [i,j]的最大加分分值。

状态计算

从最后一个合并位置,也就是根节点的位置可以将状态计算分为下面几种情况:

  • 根节点在 i i i位置,此时左子树为空,加分为 1 × 1\times 1×,得到的分数为 1 × f [ i + 1 ] [ j ] + w [ i ] 1\times f[i+1][j]+w[i] 1×f[i+1][j]+w[i]
  • 根节点在 i + 1 i+1 i+1位置,得到的分数为 f [ i ] [ i ] × f [ i + 2 ] [ j ] + w [ i + 1 ] f[i][i]\times f[i+2][j]+w[i+1] f[i][i]×f[i+2][j]+w[i+1]
  • 根节点在 k k k位置,得到的分数为 f [ i ] [ k − 1 ] × f [ k + 1 ] [ j ] + w [ k ] f[i][k-1]\times f[k+1][j]+w[k] f[i][k1]×f[k+1][j]+w[k]
  • 根节点在 j j j位置,此时右子树为空,加分为 1 × 1\times 1×,得到的分数为 f [ i ] [ j − 1 ] × 1 + w [ j ] f[i][j-1]\times 1+w[j] f[i][j1]×1+w[j]

这里 w [ i ] w[i] w[i]表示第 i i i个节点的分数。 f [ i ] [ j ] f[i][j] f[i][j]要取以上情况的最大值。

初始状态

  • 空树其加分为 1 1 1,也就是说 f [ i ] [ i − 1 ] = 1 f[i][i-1]=1 f[i][i1]=1(或者 f [ i + 1 ] [ i ] = 1 f[i+1][i]=1 f[i+1][i]=1
  • 如果区间只有一个节点,那么分值就是当前节点的分数,即 f [ i ] [ i ] = w [ i ] f[i][i]=w[i] f[i][i]=w[i]

时间复杂度

状态数为 n × n n\times n n×n,状态计算需要枚举根节点的位置 1 1 1 ~ n n n,时间复杂度为 O ( n 3 ) O(n^3) O(n3)

前序遍历

为了找到最大加分的前序遍历,就要在区间 [ i , j ] [i,j] [i,j]中找到一个根节点 k k k使得 f [ i ] [ k − 1 ] × f [ k + 1 ] [ j ] + w [ k ] f[i][k - 1]\times f[k+1][j]+w[k] f[i][k1]×f[k+1][j]+w[k]等于 f [ i ] [ j ] f[i][j] f[i][j]

对于前序遍历要先输出根节点 k k k,然后在递归遍历左子树( [ i , k − 1 ] [i,k-1] [i,k1])和右子树( [ k + 1 , j ] [k+1,j] [k+1,j])即可。

注意,如果 i i i j j j相等,说明是叶子节点,其子树的根节点就是自己。

代码实现

#include <iostream>
using namespace std;
const int N = 50;
int w[N], f[N][N];
int n;
//求区间[i,j]的前序遍历
void dfs(int i, int j)
{
    if(i > j) return; //空二叉树
    
    if(i == j) cout << i << " "; //叶子节点
    else
    {
        //枚举根节点
        for(int k = i; k <= j; k ++)
        {
            //如果以k为根节点取得加分最大值
            if(f[i][j] == f[i][k - 1] * f[k + 1][j] + w[k]) 
            {
                cout << k << " "; //输出根节点
                dfs(i, k - 1); //递归遍历左子树
                dfs(k + 1, j); //递归遍历右子树
                break;
            }
        }
    }
}

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i ++) cin >> w[i];
    //空树的加分为1
    for(int i = 1; i <= n + 1; i ++) f[i][i - 1] = 1;
    //枚举合并长度
    for(int len = 1; len <= n; len ++)
    {
        //枚举开始位置
        for(int i = 1; i + len - 1 <= n; i ++)
        {
            int j = i + len - 1; //结束位置
            if(len == 1) f[i][i] = w[i]; //初始状态
            else
            {
                //枚举其它根节点的位置
                for(int k = i; k <= j; k ++)
                    f[i][j] = max(f[i][j], f[i][k - 1] * f[k + 1][j] + w[k]);
            }            
        }
    }
    cout << f[1][n] << '\n';
    dfs(1, n);
    return 0;
}

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

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

相关文章

抖音汽车租赁小程序背后的技术挑战与解决方案

随着共享经济的不断发展&#xff0c;抖音上的汽车租赁小程序也逐渐崭露头角。然而&#xff0c;这背后涉及的技术挑战却不容小觑。本文将深入探讨抖音汽车租赁小程序的技术挑战&#xff0c;并提出相应的解决方案。 一、实时位置追踪 汽车租赁小程序的核心在于用户能够实时追踪…

JS中reduce函数的使用

一&#xff1a;函数解释 reduce()是一个对数组中的每个元素按照顺序依次执行自定义函数的方法。 就是遍历数组&#xff0c;每个元素都执行相同的方法。 二&#xff1a;实际应用 accumulator和currentValue分别用acc和cur表示哈&#xff0c;举例如下&#xff1a; 应用例子①…

赛迪生电源充电模块维修CHR-22005 RCU-202A

通信电源维修品牌&#xff1a;英可瑞,许继,艾默生,通合,动力源,九洲,华隆,合欣,泰坦,赛迪生等 直流屏模块故障和解决办法&#xff1a; 1、针对各类变电站直流屏&#xff0c;若显示交流空开跳闸&#xff0c;但并没有动作。应当检查三处地方是否正确&#xff1a; 接线是否正确…

SWT/Jface(2): 表格的编辑

前言 上节说到, 创建和渲染表格需要如下几个步骤: 接收源数据数组(也可以是单个对象或者其他集合类型): TableViewer.setInput(Object)渲染接收的数据 渲染表头: TableViewer.setLabelProvider(IBaseLabelProvider)渲染内容: TableViewer.setContentProvider(IContentProvide…

k8s-pod生命周期 4

容器环境初始化 pod 由pod 镜像来提供&#xff0c;在pod 生命周期里容器主要分为两种&#xff1a;初始化容器和主容器 初始化容器一定要成功运行并退出&#xff0c;当初始化容器运行退出完了之后主容器开始和运行 主容器开始运行的时候&#xff0c;有两个探针&#xff1a;存…

【C++初阶】STL详解(七)Stack与Queue的模拟实现

本专栏内容为&#xff1a;C学习专栏&#xff0c;分为初阶和进阶两部分。 通过本专栏的深入学习&#xff0c;你可以了解并掌握C。 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;C &#x1f69a;代码仓库&#xff1a;小小unicorn的代码仓库&…

git-3

1.如何让工作区的文件恢复为和暂存区一样&#xff1f; 工作区所作的变更还不及暂存区的变更好&#xff0c;想从暂存区拷贝到工作区&#xff0c;变更工作区(恢复成和暂存区一样的状态)&#xff0c;想到用git checkout -- 文件名 2.怎样取消暂存区部分文件的更改&#xff1f; 如…

C语言函数练习(超基础超详细)

ps:题目来源于pta平台。 1. int sum(int m, int n) {int sum0;for(int im; i<n; i){sumi;}return sum; } 2. int max(int a, int b) {if(a>b)return a;else return b; } 3. double dist( double x1, double y1, double x2, double y2 ) {return sqrt((x1-x2)*(x1…

STM32笔记---RTC

目录 一、RTC简介 二、主要特性 三、功能描述 3.1 读RTC寄存器 3.2 配置RTC寄存器 四、BKP简介 五、RTC_Init() 1. 函数BKP_ReadBackupRegister 2.RCC_LSEConfig设置外部低速晶振&#xff08;LSE&#xff09; 3.RTC基本结构 5.RTC_Init()实现 6.time.h 一、R…

靠这份求职指南找工作,稳了!

大家好&#xff0c;我是鱼皮。为了帮助朋友们更好的准备秋招&#xff0c;我们精心汇总整理了 编程导航星球 内鱼友反馈的 200 多个高频求职问题和 150 多篇面经、以及最新秋招企业投递信息表&#xff0c;解答大家的求职困惑。 一、最新秋招投递信息表 目前已汇总整理了 600 多家…

spring 是如何开启事务的, 核心原理是什么

文章目录 spring 是如何开启事务的核心原理1 基于注解开启事务2 基于代码来开启事务 spring 是如何开启事务的 核心原理 Spring事务管理的实现有许多细节&#xff0c;如果对整个接口框架有个大体了解会非常有利于我们理解事务&#xff0c;下面通过讲解Spring的事务接口来了解…

人工智能今天能为你做什么?生成式人工智能如何改变技术文档领域

▲ 搜索“大龙谈智能内容”关注GongZongHao▲ 作者 | Fabrice Lacroix 大型语言模型&#xff08;LLM&#xff09;和生成式人工智能&#xff08;GenAI&#xff09;&#xff0c;尤其是ChatGPT&#xff0c;这些是引领科技革新的新兴技术。它们不仅在科技界引起了轩然大波&#x…

项目经理超能力图鉴

大家好&#xff0c;我是老原。 在项目管理中&#xff0c;项目经理远没有大家想象中的那么光鲜亮丽&#xff0c;反而成为了背锅的代名词。 项目顺利的时候&#xff0c;老板和颜悦色、团队融洽顺利&#xff1b; 项目出问题时&#xff0c;项目经理不得不承担起责任&#xff0c;…

在.bashrc文件修改环境变量的做法

作者&#xff1a;朱金灿 来源&#xff1a;clever101的专栏 为什么大多数人学不会人工智能编程&#xff1f;>>> ~/.bashrc文件是linux下保存环境变量的系统文件。原以为使用sed命令修改.bashrc文件&#xff0c;实际上不行&#xff0c;需要使用echo命令。具体示例如下…

[shader] 光照入门(未完结。。。

反射 漫反射&#xff1a;而当物体表面粗糙时&#xff0c;我们把物体表面看作无数不同方向的微小镜面&#xff0c;则这些镜面反射出的光方向均不相同&#xff0c;这就是漫反射。 高光反射&#xff1a;我们假定物体表面光滑&#xff0c;只有一个镜面&#xff0c;那么所有的光都…

【软件工程师从0到1】- 多态 (知识汇总)

前言 介绍&#xff1a;大家好啊&#xff0c;我是hitzaki辰。 社区&#xff1a;&#xff08;完全免费、欢迎加入&#xff09;日常打卡、学习交流、资源共享的知识星球。 自媒体&#xff1a;我会在b站/抖音更新视频讲解 或 一些纯技术外的分享&#xff0c;账号同名&#xff1a;hi…

使用XHProf查找PHP性能瓶颈

使用XHProf查找PHP性能瓶颈 XHProf是facebook 开发的一个测试php性能的扩展&#xff0c;本文记录了在PHP应用中使用XHProf对PHP进行性能优化&#xff0c;查找性能瓶颈的方法。 下载 网上很多是编译安装xhprof-0.9.4版本&#xff0c;应该是用php5&#xff0c;在php8.0下编译x…

【spring(三)】AOP总结

&#x1f308;键盘敲烂&#xff0c;年薪30万&#x1f308; 目录 一、AOP相关概念 ① AOP核心思想思想&#xff1a; ② AOP专业术语&#xff1a; 二、AOP快速如入门 三、AOP工作流程 四、切入点表达式 ① 语法格式 ②支持通配符 ③书写技巧 五、通知类型 ①⭐环绕通知…

pip安装tkinter模块失败 No matching distribution found for tkinter

我想使用Python创建一个简单的桌面应用程序, 这个应用程序依赖于tkinter, 然而,当我尝试安装过程时,出现了错误。 $ pip install tkinter ERROR: Could not find a version that satisfies the requirement tkinter (from versions: none) ERROR: No matching distributio…