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

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; // 返回结果数组
    }
};

结尾

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

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Midjourney如何利用chaos控制生成图片的差异化

hello 小伙伴们&#xff0c;我是你们的老朋友——树下&#xff0c;今天分享Midjourney提示词常用参数——chaos&#xff0c;话不多说&#xff0c;直接开始~ chaos参数什么意思呢&#xff1f; 它可以用来控制我们生成图片之间的差异化程度的一个参数 通常我们在用Midjourney生…

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-1.1

前言&#xff1a; 本文是来自哔哩哔哩网站上视频“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”的学习笔记&#xff0c;在这里会记录下正点原子Linux ARM MX6ULL 开发板根据配套的哔哩哔哩学习视频所作的实验和笔记内容。本文大量的引用了正点原子哔哔哩网…

服务器 BMC(基板管理控制器,Baseboard Management Controller)认知

写在前面 工作中遇到&#xff0c;简单整理博文内容涉及 BMC 基本认知理解不足小伙伴帮忙指正 不必太纠结于当下&#xff0c;也不必太忧虑未来&#xff0c;当你经历过一些事情的时候&#xff0c;眼前的风景已经和从前不一样了。——村上春树 基板管理控制器&#xff08;BMC&…

小米一面:说说MVC与设计模式的关系

前言 大家好&#xff0c;我叫阿杆&#xff0c;不叫阿轩。 先来看看面试环节吧。 面试官&#xff1a;请说说MVC模式是基于哪种设计模式的&#xff1f; 求职者&#xff1a;MVC本身不就是一种设计模式吗&#xff1f; 面试官&#xff1a;我的意思是&#xff0c;MVC是基于23中设计…

SD-WAN为什么在亚太地区普及?

当前&#xff0c;软件定义广域网SD-WAN在亚太地区具有稳固的地位。它看起来是技术与地形的完美结合&#xff0c;因为亚太地区拥有许多大国&#xff0c;其中一些国度辽阔&#xff0c;人口分布在广阔的地理区域和偏远地区&#xff0c;如印度&#xff0c;澳大利亚&#xff0c;越南…

Introducing Meta Llama 3: The most capable openly available LLM to date

要点 今天&#xff0c;我们推出 Meta Llama 3&#xff0c;这是我们最先进的开源大型语言模型的下一代。Llama 3型号将很快在AWS&#xff0c;Databricks&#xff0c;Google Cloud&#xff0c;Hugging Face&#xff0c;Kaggle&#xff0c;IBM WatsonX&#xff0c;Microsoft Azur…

代码随想录算法训练营第四十六天| LeetCode139.单词拆分

一、LeetCode139.单词拆分 题目链接/文章讲解/视频讲解&#xff1a;https://programmercarl.com/0139.%E5%8D%95%E8%AF%8D%E6%8B%86%E5%88%86.html 状态&#xff1a;已解决 1.思路 单词明显就是物品&#xff0c;字符串s明显就是背包&#xff0c;那么问题就变成了物品能不能把背…

Three 银河系

总体效果图 当然&#xff0c;这也只是银河系的一部分&#xff0c;要想知道全景视野下的银河系是什么样的&#xff0c;只有通过科学家依据观测结果所制作的绘图来实现&#xff0c;因为银河系实在是太大了&#xff0c;目前的技术水平还无法实现全景捕捉。绘制的这张三维立体图像…

记录:阿里云服务器网站搭建(4)

Docker安装Nginx 现阶段主要目的是做一些静态资源路径的转发代理&#xff0c;相当于一个web服务器&#xff0c;tomcat也可以设置凡访问静态资源。但考虑到后续还需要作为代理服务器对域名等进行代理转发&#xff0c;所以使用nginx。 准备好要挂载的nginx配置目录 mkdir -p /m…

React-RTK

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;React篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来React篇专栏内容:React-RTK 目录 1、介绍 2、安装 3、编写RTK使用示例 4、官方提供项目包示例 创建 Redux …

ROS 2边学边练(33)-- 写一个静态广播(C++)

前言 通过这一篇我们将了解并学习到如何广播静态坐标变换到tf2&#xff08;由tf2来转换这些坐标系&#xff09;。 发布静态变换对于定义机器人底座与其传感器或非移动部件之间的关系非常有用。例如&#xff0c;在以激光扫描仪中心的坐标系中推理激光扫描测量数据是最简单的。 这…

基于人工智能的机动车号牌检测与推理系统v1.0

基于人工智能的机动车号牌检测与推理系统v1.0代码重构与实现。 目前整合3中现有算法&#xff0c;并完成阶段性改造&#xff0c;包括【传统方法检测车牌&#xff0c;SVM推理字符】、【YOLO方法检测车牌&#xff0c;SVM推理字符】、【YOLO方法检测车牌&#xff0c;CNN推理字符】&…

MapReduce案例-电影网站数据统计分析

本文适合大数据初学者学习MapReduce统计分析业务问题的步骤和基础的MapReduce编程方法&#xff0c;初步掌握Hadoop对计算任务的管理。 本文末尾有全部数据集和完整代码连接。 1.准备工作 安装Hadoop:Hadoop 3.3.2 离线安装-CSDN博客 按照好Hadoop之后要检查一下datanode运行情况…

Llama网络结构介绍

LLaMA现在已经是开源社区里炙手可热的模型了&#xff0c;但是原文中仅仅介绍了其和标准Transformer的差别&#xff0c;并没有一个全局的模型介绍。因此打算写篇文章&#xff0c;争取让读者不参考任何其他资料把LLaMA的模型搞懂。 结构 如图所示为LLaMA的示意图&#xff0c;由…

ESP32学习第一天-ESP32点亮LED,按键控制LED状态,LED流水灯

第一天使用到的函数: 函数第一个参数设置哪一个引脚&#xff0c;第二个参数设置引脚模式。 pinMode(led_pin,OUTPUT); //设置引脚模式 函数的第一个参数设置哪一个引脚&#xff0c;第二个参数设置是高电平还是低电平。 digitalWrite(led_pin,HIGH);//将引脚电平拉高 #incl…

电脑怎么拖动文件到想要的位置?电脑上拖拽没了的文件怎么找回

在日常的办公和学习中&#xff0c;电脑文件拖拽操作是每位用户都不可或缺的技能。然而&#xff0c;有时在拖动文件时&#xff0c;可能会因为误操作或其他原因&#xff0c;导致文件消失或移至未知位置。本文将详细解析如何在电脑上轻松拖动文件到指定位置&#xff0c;并为您提供…

大模型中的位置编码ALiBi,RoPE的总结和实现

目录 Alibi与旋转位置编码的比较 1. Alibi和旋转位置编码的外推性能比较 2. Alibi的处理方式 注意力线性偏置&#xff1a;ALiBi位置编码的实现 1. ALiBi的基本概念 2. ALiBi的实现方式 ALiBi位置编码的代码解读 1. 导入必要的库 2. 定义get_slopes函数 3. 定义get_al…

C++ Primer 总结索引 | 第十三章:拷贝控制

1、类可以定义构造函数&#xff0c;用来控制在创建此类型对象时做什么 类如何控制该类型对象拷贝、赋值、移动或销毁时做什么 类通过一些 特殊的成员函数 控制这些操作&#xff0c;包括&#xff1a;拷贝构造函数、移动构造函数、拷贝赋值运算符、移动赋值运算符 以及 析构函数 …

API请求报错 Required request body is missing问题解决

背景 在进行调用的时候&#xff0c;加载方法&#xff0c;提示以下错误 错误信息如下&#xff1a; {"code": 10001,"msg": "Required request body is missing: XXX","data": null,"extra": null }Required request body…

Qt使用miniblink第三方浏览器模块

文章目录 一、前言二、miniblink简介三、miniblink使用四、运行效果五、工程结构 一、前言 本文取自刘典武大师&#xff1a;Qt编写地图综合应用58-兼容多浏览器内核 用Qt做项目过程中&#xff0c;遇到需要用到浏览器控件的项目&#xff0c;可能都会绕不开一个问题&#xff0c;那…