LC-1130. 叶值的最小代价生成树(贪心、区间DP、单调栈)

news2024/12/25 9:18:13

1130. 叶值的最小代价生成树

难度中等272

给你一个正整数数组 arr,考虑所有满足以下条件的二叉树:

  • 每个节点都有 0 个或是 2 个子节点。
  • 数组 arr 中的值与树的中序遍历中每个叶节点的值一一对应。
  • 每个非叶节点的值等于其左子树和右子树中叶节点的最大值的乘积。

在所有这样的二叉树中,返回每个非叶节点的值的最小可能总和。这个和的值是一个 32 位整数。

如果一个节点有 0 个子节点,那么该节点为叶节点。

示例 1:

img

输入:arr = [6,2,4]
输出:32
解释:有两种可能的树,第一种的非叶节点的总和为 36 ,第二种非叶节点的总和为 32 。 

示例 2:

img

输入:arr = [4,11]
输出:44

提示:

  • 2 <= arr.length <= 40
  • 1 <= arr[i] <= 15
  • 答案保证是一个 32 位带符号整数,即小于 231

贪心

class Solution {
    public int mctFromLeafValues(int[] arr) {
        // 哈夫曼树 : 每次选择两个值最小的进行合并
        List<Integer> list = new ArrayList<>();
        for(int i = 0; i < arr.length; i++){
            list.add(arr[i]);
        }
        int ans = 0;
        while(list.size() > 1){
            int i = 1;
            int mul = Integer.MAX_VALUE, tmp = 1;
            while(i < list.size()){
                if(mul > list.get(i) * list.get(i-1)){
                    mul = list.get(i) * list.get(i-1);
                    tmp = i;
                }
                i += 1;
            }
            // 答案加上当前最小乘积,并删除两个数中较小的节点
            ans += mul;
            if(list.get(tmp) > list.get(tmp-1)) list.remove(tmp-1);
            else list.remove(tmp);
        }
        return ans;
    }
}

记忆化搜索 ==> 动态规划

打开思路的几个关键点:

1、对于每个区间[L, R]的叶子结点,最终会汇聚成一个根节点,但这个根节点会根据[L, R]中的子区间划分方式的不同而不同,我们要做的就是要求其中的最小的

2、不要纠结“树的形状”、“树有几层”等等的关于“树”相关的东西,区间划分一旦定了,树就定了,我们需要将“树”的概念抽象掉,唯一核心的就是一个区间对应2个子树和一个根节点!!!

3、对于任意一个区间[L, R],一定是将其划分成2个区间,分别对应最终根节点的左右子树。而根节点的值就是两个区间各自的最大值乘积。

4、对于根节点的子树中非叶子结点的累加值,其实就是在递归子问题中求得了(也可以认为是状态转移方程,因为记忆化搜索和DP本来就是一一对应的)

https://leetcode.cn/problems/minimum-cost-tree-from-leaf-values/solution/qu-jian-dp-dong-tai-gui-hua-die-dai-xie-g4jac/

class Solution {
    public int mctFromLeafValues(int[] arr) {
        //区间dp:
        //先计算每个区间内的最大值,用于计算每个非叶子节点的值
        //分成左右两个子树,得到两个子树中的非叶子节点的和的最小值,相加再加上当前树的root结点的值(上面得到的每个区间的最大值可以计算得到)
        int n = arr.length;
        // 预处理区间最大值,这样可以O(1) 的获取每个区间的最大值
        int[][] g = new int[n][n];
        for(int i = 0; i < n; i++)
            for(int j = 0; j < n; j++)
                for(int k = i; k <= j; k++)
                    g[i][j] = Math.max(g[i][j], arr[k]);
        
        int[][] f = new int[n][n];
        // 区间DP
        for(int len = 1; len <= n; len++){ // 枚举区间长度
            for(int i = 0; i + len - 1 < n; i++){ // 枚举区间起点
                int j = i + len - 1; // 区间终点
                f[i][j] = Integer.MAX_VALUE; // 因为求区间最小值,初始化为inf
                if(len == 1) f[i][j] = 0;
                else{
                    for(int k = i; k < j; k++)
                        f[i][j] = Math.min(f[i][j], f[i][k] + f[k+1][j] + g[i][k] * g[k+1][j]);
                }
            }
        }
        return f[0][n-1];
    }
}

单调栈

https://leetcode.cn/problems/minimum-cost-tree-from-leaf-values/solution/1130-cchao-100de-dan-diao-zhan-tan-xin-j-7zwh/

贪心的单调栈解法

  • 最优情况:其实就是严格有序的,从小到大来乘然后累加,对应情况就是类似[4,3,2,1]
  • 指导原则:严格按照arr的顺序下,每次总是用乘积最小两个数一起乘
  • 使用一个单调递减的栈来实现局部有序
    • 不满足有序:一旦发现当前元素大于栈顶,则弹出栈顶元素,(这个元素作为叶子节点,必须要做乘法)
    • 此时需要考虑是新的栈顶 和 当前元素 的最小值 去乘,乘的结果会做累加
    • 弹出全部比它小的元素后,插入新的元素到栈顶
  • 坑:最后栈里可能存有排序好的数字(本质就是最优的解),需要依次弹出一个并累加乘积
  • 整个过程中累加结果就是结果+
class Solution {
    public int mctFromLeafValues(int[] arr) {
        // 维护一个单调递减的栈
        Deque<Integer> dq = new ArrayDeque<>();
        // 预先插入一个极大值,后续无需判断边缘情况,如s为空
        dq.addLast(Integer.MAX_VALUE);
        int n = arr.length;
        int res = 0;
        for(int num : arr){
            while(num > dq.peekLast()){
                int cur = dq.pollLast();
                // 这里要考虑可能接下来栈顶数字比arr[i]还要小,我们需要用这个更小数字来乘
                res += cur * Math.min(dq.peekLast(), num);
            }
            dq.addLast(num);
        }
        // 最后弹出剩余元素(除了预先插入 INT_MAX,所以最小是3才考虑)
        while(dq.size() >= 3){
            int cur = dq.pollLast();
            res += cur * dq.peekLast();
        }
        return res;
    }
}

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

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

相关文章

chatgpt赋能python:Python中的逆序操作

Python 中的逆序操作 在 Python 中&#xff0c;逆序&#xff08;reverse&#xff09;操作指的是将一个序列的元素顺序反转&#xff0c;也即将序列中最后一个元素变成第一个&#xff0c;倒数第二个元素变成第二个&#xff0c;以此类推。逆序有很多实际用途&#xff0c;比如根据…

基于C语言的平衡二叉树操作(包含完整代码)

平衡二叉树的定义: 为避免树的高度增长过快&#xff0c;降低二叉排序树的性能&#xff0c;规定在插入和删除二叉树结点时&#xff0c;要保证任意结点的左、右子树高度差的绝对值不超过1&#xff0c;将这样的二义树称为平衡二叉树AVL (Balanced Binary Tree),简称平衡树。 平衡…

【源码解析】流控框架Sentinel源码深度解析

前言 前面写了一篇Sentinel的源码解析&#xff0c;主要侧重点在于Sentinel流程的运转原理。流控框架Sentinel源码解析&#xff0c;侧重点在整个流程。该篇文章将对里面的细节做深入剖析。 统计数据 StatisticSlot用来统计节点访问次数 SpiOrder(-7000) public class Statis…

PCL 改进点云双边滤波算法

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 我们先来回顾一下之前该算法的计算过程,在二维图像领域中,双边滤波算法是通过考虑中心像素点到邻域像素点的距离(一边)以及像素亮度差值所确定的权重(另一边)来修正当前采样中心点的位置,从而达到平滑滤波效果。…

PHPMySQL基础(五):模拟登录后跳转+会话存储功能实现

PHP&MySQL基础&#xff08;一&#xff09;:创建数据库并通过PHP进行连接_长风沛雨的博客-CSDN博客 PHP&MySQL基础&#xff08;二&#xff09;:通过PHP对MySQL进行增、删、改、查_长风沛雨的博客-CSDN博客 PHP&MySQL基础&#xff08;三&#xff09;:处理查询SQL返…

一图看懂 tqdm 模块:一个可在循环和命令行中使用的快速、可扩展的进度条,资料整理+笔记(大全)

本文由 大侠(AhcaoZhu)原创&#xff0c;转载请声明。 链接: https://blog.csdn.net/Ahcao2008 一图看懂 tqdm 模块&#xff1a;一个可在循环和命令行中使用的快速、可扩展的进度条&#xff0c;资料整理笔记&#xff08;大全&#xff09; &#x1f9ca;摘要&#x1f9ca;模块图&…

软考高级架构师笔记-5计算机网络

目录 1. 前言 & 考情分析2. 网络功能和分类2.1 通信技术3. OSI七层模型及协议3. 1 局域网和广域网协议3. 2 协议3. 3 交换技术、路由、传输介质4 IP地址5 网络存储技术6 其它考点8. 结语1. 前言 & 考情分析 前文回顾: 软考高级架构师笔记-1计算机硬件软考高级架构师笔…

chatgpt赋能python:Python中未定义变量的默认值

Python中未定义变量的默认值 在Python编程中&#xff0c;有时候我们会使用未经定义的变量。如果这些变量没有被定义&#xff0c;那么它们将没有任何值。在这篇文章中&#xff0c;我们将讨论Python中未定义变量默认值的问题&#xff0c;并深入研究为什么这些默认值如此重要。 …

华为OD机试真题B卷 Java 实现【寻找关键钥匙】,附详细解题思路

一、题目描述 小强正在参加《密室逃生》游戏&#xff0c;当前关卡要求找到符合给定 密码K&#xff08;升序的不重复小写字母组成&#xff09;的箱子&#xff0c;并给出箱子编号&#xff0c;箱子编号为1~N。 每个箱子中都有一个字符串s&#xff0c;字符串由大写字母&#xff0…

改进YOLOv5,利用HRNet高分辨率特征金字塔的全新物体检测突破

目录 一、介绍1、物体检测的背景与重要性2、HRNet和YOLOv5的概述&#xff08;1&#xff09;HRNet的概述&#xff08;2&#xff09;YOLOv5的概述 二、HRNet的架构1、HRNet的基本单元2、HRNet的高分辨率特征金字塔3、HRNet的体系结构4、HRNet的特点5、HRNet的局限性 三、YOLOv5的…

chatgpt赋能python:Python中转化为列表的详细介绍

Python中转化为列表的详细介绍 Python是一门高级编程语言&#xff0c;它使用起来简单易学&#xff0c;被广泛应用于大数据处理、科学计算、机器学习等领域。在Python编程中&#xff0c;列表是一种非常重要的数据结构&#xff0c;它允许我们存储和操作一组数据&#xff0c;并且…

jenkins —— pipeline基础语法与示例

一、Jenkins介绍 二、Jenkins Pipeline介绍 Jenkins Pipeline总体介绍 1.Pipeline 是Jenkins 2.X核心特性&#xff0c;帮助Jenkins实现从CI到CD与DevOps的转变 2.Pipeline 简而言之&#xff0c;就是一套运行于Jenkins上的工作流框架&#xff0c;将原本独立 运行于单个或者多个…

GPT-4 的 6 个最佳使用场景

https://www.howtogeek.com/884077/best-uses-for-chatgpt-4/ 作者&#xff1a;SYDNEY BUTLER 无论是在 ChatGPT 中还是通过 API&#xff0c;对 OpenAI 的 GPT-4 模型的访问比 GPT-3.5 限制更多。这意味着你需要慎重考虑在何种情况下使用 GPT-4&#xff0c;并选择性地将最适合…

浙大知识图谱基础:学习笔记

0 基础知识 知识图谱中&#xff0c;知识的结构化表示主要有符号表示和向量表示两类方法。符号表示包括&#xff1a;一阶谓词逻辑&#xff0c;语义网络&#xff0c;描述逻辑和框架系统等。当前主要采用基于图的符号化知识表示&#xff0c;最常用的是有向标记图。 有向标记图分为…

SpringBoot统一功能处理(统一处理用户登陆权限验证、统一异常处理以及统一数据返回格式)

目录 1. SpringBoot统一功能处理简介 2. 统一处理用户登陆验证 2.1 原生SpringAOP实现统一登陆验证的问题 2.2 Spring拦截器实现用户统一登陆验证 2.3 扩展: 统一访问前缀添加 3. 统一异常处理 4. 统一数据返回格式 4.1 统一数据返回格式的必要性 4.2 实现统一数据返…

C++ vector类成员函数介绍

目录 &#x1f914;vector模板介绍&#xff1a; &#x1f914;特点&#xff1a; &#x1f914;vector的成员函数&#xff1a; &#x1f50d;vector构造函数&#xff1a; &#x1f50d;vector赋值函数 &#x1f50d;vector容器的判断函数 resize函数的重点内容&#xff1a; …

chatgpt赋能python:Python中的并:优化代码执行效率的利器

Python中的并&#xff1a;优化代码执行效率的利器 Python作为一种优秀的编程语言&#xff0c;被广泛使用于各种各样的项目中。然而&#xff0c;随着程序的不断扩张&#xff0c;代码的复杂度日益增加&#xff0c;执行效率也愈加遇到了严峻的挑战。在这种情况下&#xff0c;Pyth…

Linux系统下imx6ull QT编程—— Ubuntu 下编写程序(一)

Linux QT编程 文章目录 Linux QT编程前言一、C简介二、C环境设置1.安装编译 C 语言和 C的环境。2.创建文件编写代码3.编译运行代码 总结 前言 绍在 Ubuntu 在终端窗口下使用 vi/vim 编辑一个 C源文件。通过编写最简单的示例“Hello,World QCX”。 一、C简介 C &#xff08;c…

【分布式应用】ELK企业级日志分析系统

一、ELK 简介 ELK平台是一套完整的日志集中处理解决方案&#xff0c;将 ElasticSearch、Logstash 和 Kiabana 三个开源工具配合使用&#xff0c; 完成更强大的用户对日志的查询、排序、统计需求。 1.1 ELK各组件介绍 ElasticSearch&#xff1a; 是基于Lucene&#xff08;一个…

Scala初识

1.scala简介 是一种多范式的编程语言&#xff0c;其设计的初衷是要集成面向对象编程和函数式编程的各种特性。Scala运行于Java平台&#xff08;Java虚拟机&#xff09;并兼容现有的Java程序。 scala特点 1.Scala是面向对象的 Scala是一种纯粹的面向对象语言&#xff0c;每一个…