【六十四】【算法分析与设计】699. 掉落的方块,离散化操作,线段树优化,区间查询sum+区间更新update

news2025/1/15 17:37:40

699. 掉落的方块

在二维平面上的 x 轴上,放置着一些方块。

给你一个二维整数数组 positions ,其中 positions[i] = [left(i), sideLength(i)] 表示:第 i 个方块边长为 sideLength(i) ,其左侧边与 x 轴上坐标点 left(i) 对齐。

每个方块都从一个比目前所有的落地方块更高的高度掉落而下。方块沿 y 轴负方向下落,直到着陆到 另一个正方形的顶边 或者是 x 轴上 。一个方块仅仅是擦过另一个方块的左侧边或右侧边不算着陆。一旦着陆,它就会固定在原地,无法移动。

在每个方块掉落后,你必须记录目前所有已经落稳的 方块堆叠的最高高度

返回一个整数数组 ans ,其中 ans[i] 表示在第 i 块方块掉落后堆叠的最高高度。

示例 1:

输入:positions = [[1,2],[2,3],[6,1]] 输出:[2,5,5] 解释: 第 1 个方块掉落后,最高的堆叠由方块 1 组成,堆叠的最高高度为 2 。 第 2 个方块掉落后,最高的堆叠由方块 1 和 2 组成,堆叠的最高高度为 5 。 第 3 个方块掉落后,最高的堆叠仍然由方块 1 和 2 组成,堆叠的最高高度为 5 。 因此,返回 [2, 5, 5] 作为答案。

示例 2:

输入:positions = [[100,100],[200,100]] 输出:[100,100] 解释: 第 1 个方块掉落后,最高的堆叠由方块 1 组成,堆叠的最高高度为 100 。 第 2 个方块掉落后,最高的堆叠可以由方块 1 组成也可以由方块 2 组成,堆叠的最高高度为 100 。 因此,返回 [100, 100] 作为答案。 注意,方块 2 擦过方块 1 的右侧边,但不会算作在方块 1 上着陆。

提示:

  • 1 <= positions.length <= 1000

  • 1 <= left(i) <= 10(8)

  • 1 <= sideLength(i) <= 10(6)

离散化+暴力

1.

对于每一个position值,[1,2]表示[1,1+2]区间上落下一个边长为2的正方形.

[2,3]表示[2,2+3]区间上落下一个边长为3的正方形.

[6,1]表示[6,6+1]区间上落下一个边长为3的正方形.

2.

对于每一个position值,可以抽象出左右边界和正方形的边长.[left,right],边长h.

maxcount数组存储每个点上的最大高度.

由于点没有办法表示图像,长度.因此可以定义index表示[index,index+1)的区间.

maxcount[1]=2,表示[1,2)区间上最大的高度是2.以此类推

那么对于每一个position,position[i][0]==left,position[i][0]+position[i][1]-1==right.边长为position[i][1].

3.

只需要对于每一个position方块,遍历left到right找到最大值记为maxh,此时更新left到right所有值为maxh+position[i][1].

然后把此时所有区间的最大高度值尾插到ret数组中.

因此还需要一个变量存储所有区间的最大高度值.

离散化

1.

我们知道所有方块对应的下标,第一个方块对应的left,right,第二个方块对应的left,right......

将这些下标排序+去重.存储到map中.

也就是直接将这些数据加入到map中即可,因为map自动排序+去重.

2.

将所有需要用到的下标,point加入到map后,依次给这些数据设置映射值,第一个元素映射0,第二个元素映射1,第三个元素映射3,以此类推.

压缩坐标.因为原来的point的值我们并不关心是多少,我们只关心每一个point有一个maxcount记录最大值.

因此对于每一个point映射下标,对应maxcount的下标.

暴力

然后暴力求解即可.

 
class Solution {
public:
    vector<int> fallingSquares(vector<vector<int>>& positions) {
        map<int, int> nums_index; // 使用map来记录每个方块的左右边界对应的高度索引
        vector<int> ret; // 存储结果的数组
        for (auto& x : positions) {
            nums_index[x[0]]; // 记录左边界对应的高度索引
            nums_index[x[0] + x[1] - 1]; // 记录右边界对应的高度索引
        }

        int index = 0;
        for (auto& x : nums_index) {
            x.second = index++; // 为所有的高度索引分配唯一的编号
        }

        vector<int> maxcount(index); // 存储每个高度索引对应的最大高度
        int maxh = INT_MIN;
        for (auto& x : positions) {
            int left = nums_index[x[0]]; // 获取左边界对应的高度索引
            int right = nums_index[x[0] + x[1] - 1]; // 获取右边界对应的高度索引

            int max1 = INT_MIN;
            for (int i = left; i <= right; i++) {
                max1 = fmax(maxcount[i], max1); // 找到左右边界中的最大高度
            }
            int newh = max1 + x[1]; // 计算掉落后的新高度

            for (int i = left; i <= right; i++) {
                maxcount[i] = newh; // 更新每个高度索引对应的高度
            }
            if (newh > maxh)
                maxh = newh; // 更新最大高度
            ret.push_back(maxh); // 将最大高度加入结果数组
        }

        return ret; // 返回结果数组
    }
};

离散化+线段树

线段树

在暴力过程中,我们遍历left到right区间找区间最大值,然后全部加上边长,再把所有区间的最大高度尾插到ret数组中.

我们暴力查询区间max,暴力更新区间所有值.时间复杂度是O(N).

线段树优化这两个过程,线段树区间查询和区间更新操作都是O(logN).

线段树代码分析

1.

成员变量,一般来说要有一个arr数组,对应nums数组,区别是arr数组元素下标从1开始,也就是0位置不用,从1开始的元素依次对应nums数组的值.

size遍历存储数组大小.

treenode表示树的节点信息.

tree表示arr数组对应的线段树结构.

2.

treenode节点信息,maxh对应线段树区间查询的信息.

int change; // 变化值 int isupdate; // 是否需要更新

对应线段树区间更新的操作

表示任务值,和任务是否存在.

3.

pushup函数,用已经维护好的左右孩子信息,维护当前节点的信息.

维护的信息是线段树区间查询的信息.

4.

pushdown函数,用于下发一层任务,如果当前节点有任务,就下发,下发任务需要更新左右孩子的信息.

线段树区间查询的信息以及有关任务的信息.

也就是treenode中所有的信息.

5.

query查询函数,树结构对应是递归函数,用于查询L~R区间中的sum元素和.

如果当前节点对应的区间是L~R的一部分,返回当前节点的sum值.

如果当前节点和L~R没有重叠,return 0.

如果当前节点和L~R有一部分重叠,return 左孩子中某个节点区间是L~R一部分的sum值.或者renturn 右孩子中某一个节点区间是L~R一部分的sum值.

请注意,确保能够准确的实现递归逻辑,递归查询,去孩子节点查询的时候,必须把当前任务下发.如果有任务就下发,没有任务就不下发.

6.

update区间更新函数,树结构对应是递归函数,用于表示L~R区间更新为C.

如果当前节点对应的区间是L~R的一部分,维护当前节点所有查询信息和此次操作的信息.

如果当前节点和L~R没有重叠,return .表示不需要维护信息

如果当前节点和L~R有一部分重叠,更新左子树,更新右子树,更新自己.

同样的更新左右子树的时候,需要把旧任务下发一层.再更新

 
class SegmentTree {
public:
    int size; // 线段树数组的大小
    struct treenode {
        int maxh; // 节点对应的最大高度

        int change; // 变化值
        int isupdate; // 是否需要更新
    };
    vector<treenode> tree; // 线段树节点数组

    void pushup(int i) { // 更新父节点的最大高度
        tree[i].maxh = max(tree[i << 1].maxh, tree[i << 1 | 1].maxh);
    }
    void pushdown(int i) { // 下推更新
        if (tree[i].isupdate) {
            tree[i << 1].maxh = tree[i << 1 | 1].maxh = tree[i].change;
            tree[i << 1].change = tree[i << 1 | 1].change = tree[i].change;
            tree[i << 1].isupdate = tree[i << 1 | 1].isupdate = true;

            tree[i].isupdate = false;
        }
    }
    int query(int L, int R) { // 查询
        return _query(L + 1, R + 1, 1, size - 1, 1);
    }
private:
    int _query(int L, int R, int l, int r, int rt) { // 内部查询函数
        if (r < L || R < l) return 0;
        if (L <= l && r <= R) {
            return tree[rt].maxh;
        }
        pushdown(rt);
        int mid = (l + r) >> 1;
        return max(_query(L, R, l, mid, rt << 1), _query(L, R, mid + 1, r, rt << 1 | 1));
    }
public:
    void update(int L, int R, int C) { // 更新
        _update(L + 1, R + 1, C, 1, size - 1, 1);
    }
private:
    void _update(int L, int R, int C, int l, int r, int rt) { // 内部更新函数
        if (r < L || R < l) return;
        if (L <= l && r <= R) {
            tree[rt].maxh = C;
            tree[rt].isupdate = true;
            tree[rt].change = C;
            return;
        }

        pushdown(rt);
        int mid = (l + r) >> 1;
        _update(L, R, C, l, mid, rt << 1);
        _update(L, R, C, mid + 1, r, rt << 1 | 1);
        pushup(rt);
    }
public:
    SegmentTree(int n) { // 构造函数
        size = n + 1;
        tree.resize(size << 2);
    }
};

class Solution {
public:
    vector<int> fallingSquares(vector<vector<int>>& positions) {
        // 初始化操作
        int n = positions.size();
        map<int, int> point_index; // 统计点的索引
        for (auto& x : positions) {
            point_index[x[0]];
            point_index[x[0] + x[1] - 1];
        }
        int index = 0;
        for (auto& x : point_index) {
            x.second = index++;
        }

        SegmentTree t(point_index.size()); // 初始化线段树
        // 开始解题
        vector<int> ret; // 存储结果的数组
        int maxh = INT_MIN;
        for (auto& x : positions) {
            int left = point_index[x[0]]; // 获取左边界对应的高度索引
            int right = point_index[x[0] + x[1] - 1]; // 获取右边界对应的高度索引
            int h = x[1]; // 方块高度
            int cur_maxHight = t.query(left, right); // 查询当前区间的最大高度
            cur_maxHight = cur_maxHight + h; // 计算新高度
            maxh = max(maxh, cur_maxHight); // 更新最大高度
            ret.push_back(maxh); // 将最大高度加入结果数组
            t.update(left, right, cur_maxHight); // 更新区间
        }

        return ret; // 返回结果数组
    }
};

结尾

最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。

同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。

谢谢您的支持,期待与您在下一篇文章中再次相遇!

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

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

相关文章

SJMG—650T型微机控制静载锚固试验机

一.概述 SJMG-650T型微机控制静载锚固试验机是预应力锚具生产企业的出厂检验和型式试验、大型工程使用单位的锚具进场验收、产品质量监督部门对预应力锚具组装件检测的专用设备。该设备由宽调速范围数字伺服阀及微机测控技术&#xff0c;组成全自动闭环调速控制系统&#xff0…

IDM 平替 Gopeed Flutter 开源免费下载工具

IDM 平替 Gopeed Flutter 开源免费下载工具 视频 https://youtu.be/m206G5lVXPM https://www.bilibili.com/video/BV1Lz421k7Zp/ 前言 原文 https://ducafecat.com/blog/flutter-gopeed-downloader-idm-replace https://flutter.ducafecat.com/github/repo/GopeedLab/gopeed…

坚蛋运动新质生产力实践——“AI健康”战略引领产品和服务创新

进入AI时代&#xff0c;全球互联网企业均开启了以大模型及其应用为代表的第四次工业革命的激烈竞赛。坚蛋运动已在全国范围内布局300门店&#xff0c;预计实现2024年500、2025年1000门店&#xff0c;作为国内运动健康产业的头部品牌&#xff0c;坚蛋运动率先提出并推动“AI健康…

Docker 基本认识

一 国内&#xff1a; 阿里云 提供ECS&#xff08;Elastic Compute Service&#xff09;弹性计算服务&#xff0c;包括通用型、计算型、内存型等多种实例&#xff0c; 满足不同应用场景的需求。 支持按需付费、包年包月等多种计费方式。 腾讯云&#xff1a; 提供CVM&#…

重磅发布 | 《网络安全专用产品指南》(第一版)

2017年6月1日&#xff0c;《中华人民共和国网络安全法》正式实施&#xff0c;明确规定“网络关键设备和网络安全专用产品应当按照相关国家标准的强制性要求&#xff0c;由具备资格的机构安全认证合格或者安全检测符合要求后&#xff0c;方可销售或者提供。国家网信部门会同国务…

vue3 watch监听

Watch在vue3中是一个组合API&#xff0c;可以多次调用&#xff0c;它有三个参数&#xff1a; Params1&#xff1a;被监听的变量&#xff0c;可以是一个数组&#xff0c;存放多个变量。 Params2&#xff1a;回调函数&#xff0c;监听的数据有变化时调用&#xff0c;回调函数中有…

IUG-CF论文精读

Neural collaborative filtering with ideal user group labels &#xff08;具有理想用户组标签的神经协同过滤&#xff09; 论文地址&#xff1a;https://www.sciencedirect.com/science/article/pii/S0957417423023898 摘要&#xff1a; 人口统计信息是推荐系统(RSs)的关键…

机器学习(四)之无监督学习

前言&#xff1a; 前面写了监督学习的几种算法&#xff0c;下面就开始无监督啦&#xff01; 如果文章有错误之处&#xff0c;小伙伴尽情在评论区指出来&#xff08;嘿嘿&#xff09;&#xff0c;看到就会回复的。 1.聚类&#xff08;Clustering&#xff09; 1.1 概述&#xff…

javaWeb-异常处理和事务管理

异常处理 我们的代码出现异常之后&#xff0c;异常会向上抛出&#xff0c;直到抛给Spring框架&#xff0c;会去响应一个错误结果 我们要想处理异常&#xff0c;可以在Controller的方法中去捕获异常&#xff0c;但这样做很繁琐&#xff0c;常常我们会定义一个全局异常处理器来…

每日两题 / 438. 找到字符串中所有字母异位词 238. 除自身以外数组的乘积(LeetCode热题100)

438. 找到字符串中所有字母异位词 - 力扣&#xff08;LeetCode&#xff09; 记录p串每个字符出现次数 维护与p串等长的滑动窗口&#xff0c;记录其中每个字符的出现次数 每次滑动后将当前次数与p串的次数比较即可 class Solution { public:vector<int> findAnagrams(s…

Java——继承与组合

和继承类似, 组合也是一种表达类之间关系的方式, 也是能够达到代码重用的效果。组合并没有涉及到特殊的语法 (诸如 extends 这样的关键字), 仅仅是将一个类的实例作为另外一个类的字段。 继承表示对象之间是is-a的关系&#xff0c;比如&#xff1a;狗是动物&#xff0c;猫是动…

API接口的用途以及接入示例

API接口的主要用途是允许不同的软件系统之间进行通信和数据交换。具体来说&#xff0c;API接口可以用于以下几个方面&#xff1a; 数据传输和交换&#xff1a;API接口可以用于不同系统之间的数据传输和交换&#xff0c;例如将数据从一个系统传递到另一个系统&#xff0c;或者从…

Modbus转Profinet网关接称重设备与工控机通讯

Modbus转Profinet网关&#xff08;XD-MDPN100&#xff09;是一种能够实现Modbus协议和Profinet协议之间转换的设备。Modbus转Profinet网关可提供单个或多个RS485接口&#xff0c;使得不同设备之间可以顺利进行通信&#xff0c;进一步提升了工业自动化程度。 通过使用Modbus转Pr…

Linux加强篇-Vim编辑器

目录 ⛳️推荐 Vim文本编辑器 编写简单文档 配置主机名称 配置网卡信息 配置软件仓库 ⛳️推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站 Vim文本编辑器 在Linux系统中一切都…

嵌入式面试-回答UART

说明&#xff1a; 此文章是在阅读了一些列面试相关资料之后对于一些常见问题的整理&#xff0c;主要针对的是嵌入式软件面试中涉及到的问答&#xff0c;努力精准的抓住重点进行描述。若有不足非常欢迎指出&#xff0c;感谢&#xff01;在总结过程中有些答案没标记参考来源&…

【Camera Sensor Driver笔记】五、点亮指南之Actuator配置

<slaveInfo> actuatorName dw9714v dirver IC 型号 slaveAddress 0x18 i2c write address i2cFrequencyMode FAST i2c 操作频率(400KHz) actuatorType VCM/BIVCM 马达类型 BIVCM&#xff08;中置马达&#xff…

一个小时学习javaScript

1 简介 1.1 什么是javascript JavaScript 是一种广泛使用的编程语言&#xff0c;最初被设计来增加网页的交互性&#xff0c;让用户能够与网页上的元素进行互动。自从1995年被引入以来&#xff0c;JavaScript已经发展成为Web开发中不可或缺的一部分&#xff0c;并且其用途已经…

qdbus

qdbus ## 一些简单的使用<font color red>(重要)QtDBus编程1、创建服务并创建对象2、通过QDBusMessage访问Service3、通过QDBusInterface 访问Service4、从D-Bus XML自动生成Proxy类5、使用Adapter注册Object6、自动启动Service qdbus是对dbus的进一步封装&#xff0c;d…

MS1000TA超声波测量模拟前端

产品简述 MS1000TA 是一款超声波测量模拟前端芯片&#xff0c;广 泛应用于汽车工业和消费类电子。该芯片具有高度 的灵活性&#xff0c;发射脉冲个数、频率、增益及信号阈值 均可配置。同时&#xff0c;接收通道参数也可以灵活配置&#xff0c; 从而适用于不同尺寸容器、不…

qt;lt;等xml|Html转义字符

在写Android布局文件时&#xff0c;左右尖括号<>&#xff0c;括号在XML中没办法直接使用&#xff0c;需要进行转义&#xff0c;收集一些转义符&#xff0c;以便查询使用。 常用表&#xff1a; **对于文章出现的任何问题请大家批评指出&#xff0c;一定及时修改 **可联系…