算法41:掉落的方块(力扣699题)----线段树

题目:https://leetcode.cn/problems/falling-squares/description/

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

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

每个方块都从一个比目前所有的落地方块更高的高度掉落而下。方块沿 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 上着陆。

在算法40中,我们介绍了线段树以及使用线段树求累加和的案例。他们使用的都是一维数据,区间很好划分。而这一题是二维数组,二维数组中的每个一维数组第一个元素代表起始位置,第二个元素代表高度,思来想去,还是不知道如何去划分区间。

1. 之前的区间跟数据没关系,只是跟数据的位置有关系;而本题尝试以二维数组作为一个单元数据,没法划分;

2. 以X轴横坐标划分区间;假设数据量很大,而且数据范围也很大,比如{{1,10}, {10,1000000},{100000000, 100000000000000000}}; 这样的数组该如何去划分区间呢?貌似也走不通。

离散化技巧:

假设二维数组为 {

{300, 5000},{17, 67300},{4500, 5000万}

}

我们把这些坐标进行搜集并排序得到 17,300,4500,5000,67300,5000万; 按照线段树的思路给下标:

下标 :0123456
忽略173004500500067300500万

如果按照这样的思路,我们就可以得到:

{300, 5000} = [2, 4] 区间

{17, 67300} = [1, 5] 区间

{4500, 5000万} = [3, 6] 区间

这样的技巧就叫做离散化技巧,确实很牛逼。我也是思考了很久,最终看了大神的解释才弄懂的。

本题分析;

假设二维数组中有一个数组 {1,3},1代表开始位置,3代表长度;得到开始、结束位置{1,4};

此时,又来一个数组 {4,2};代表4是开始位置,2是长度;得到开始、结束位置{4,6}

那么此时问题就来了,{1,4} 和 {4,6}存在相同的4;但是本题方块却可以正常紧挨着降落在一排;如果按照区间来划分算高度,4这个位置会出问题:

想要解决这样的问题,结束的位置坐标往左推一个就可以解决;

{1,4} 实际上代表的是 [1,4) 左闭右开; 给转换成 [1,3]

{4,6}实际上代表的是 [4,6) 左闭右开;给转换成 [4,5]

本题中的坐标都只能是整数,这是隐藏信息;因此,以上的转换是正确的。

区间的确定:

本题中,区间是根这些坐标有关系的;利用离散化技巧以及上方关于区间的分析可得;

我们以本题中给定的图片进行分析

第一个数是[1,3),我们得到[1,2]

第二个数是[2,5), 我们得到[2,4]

第三个数是[6,7), 我们得到[6,6]

无重复收集这些坐标信息,得到 {1,2,4,6},分别给个下标

下标01234
忽略1246

这样,我们就知道了第一个方块在 1-2区间;第二个方块在 2-3区间;第三个方块在4区间;

本题是算最大高度的,因此无需原始数组;max数组全部为0即可;

当第一个方块{1,3}落下的时候,{1,3} 对应 1-2区间,也就是跟节点的左子树;那么左子树的高度就为 3; 跟节点取左、右子节点的最大值;

第二个方块{2,5}落下,{2,5}对应 2-3区间;此时,1-2区间的3下方到左、右子节点;

获取到2-3区间的最大值;目测是3;那么此时第二块方块落下以后,2-3区间的最大值就为 3+2 = 5;根节点取左、右子树最大值,也为5;

第三个方块{6,7}对应4区间;那么4区间高度就为1; 取值结果没有变化

目测整个线段树的最大高度会一直汇总到树的顶部,那么只要获取树的顶部数据,就可以获取到最大值了;

package code04.线段树_02;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.TreeSet;

/**
 * 力扣 699题 :掉落的方块
 * https://leetcode.cn/problems/falling-squares/description/
 */
public class Code02_FallingSquares {

    class SegmentTree {
        int[] max;
        int[] lazy;
        boolean[] update;

        public SegmentTree(int size) {
            max = new int[size * 4];
            lazy = new int[size * 4];
            update = new boolean[size * 4];
        }

        //统计index节点,从左、右节点中选取较大的
        public void count(int index) {
            max[index] = Math.max(max[index * 2], max[index * 2 + 1]);
        }

        public void pushDown(int curIndex)
        {
            //判断curIndex是否有懒数据没更新到子节点
            if (lazy[curIndex] != 0) {
                //左、右子区间加上懒更新的数据
                max[curIndex * 2] = lazy[curIndex];
                max[curIndex * 2 + 1] =  lazy[curIndex];

                //左、右子树区间记录懒数据
                lazy[curIndex * 2] = lazy[curIndex];
                lazy[curIndex * 2 + 1] =  lazy[curIndex];

                //原curIndex数据已经下放到子区间了,此处需要重置为0
                lazy[curIndex] = 0;
            }
        }


        public void add(int left, int right, int curIndex, int start, int end, int value)
        {
            if (start <= left && end >= right) {
                //默认max中全部为高度全部为0. 那么下降一个方块,高度就选取大的加上下架的方块高度 value
                max[curIndex] = value;
                lazy[curIndex] = value;
                return;
            }

            int mid = (left + right)/2;
            //如果当前节点curIndex之前有懒的数据,那么把curIndex之前的懒
            //数据下放到子节点区间
            pushDown(curIndex);

            if (start <= mid) {
                add(left, mid, curIndex * 2, start, end, value);
            }

            if (end > mid) {
                add(mid + 1, right, curIndex * 2 + 1, start, end, value);
            }

            //重新汇总curIndex节点的最大值
            count(curIndex);
        }

        public int query(int left, int right, int curIndex, int start, int end)
        {
            if (start <= left && end >= right) {
                return max[curIndex];
            }

            int max = 0;
            int mid = (left + right) / 2;
            pushDown(curIndex);
            if (start <= mid) {
                int ans = query(left, mid, curIndex * 2, start, end);
                max = Math.max(ans, max);
            }

            if (end > mid) {
                int ans = query(mid + 1, right, curIndex * 2 + 1, start, end);
                max = Math.max(ans, max);
            }
            return max;
        }

        public int getTreeMaxHeight(){
            return max[1];
        }
    }


    public HashMap<Integer, Integer> index(int[][] positions)
    {
        TreeSet<Integer> pos = new TreeSet<>();
        //离散化过程,统计开始、结束区间的坐标。
        //不管数组长度为多少,最终都是落在这些区间中的
        for (int[] arr : positions) {
            pos.add(arr[0]);
            pos.add(arr[0] + arr[1] - 1);
        }

        int index = 1;
        HashMap<Integer, Integer> map = new HashMap<>();
        //给每个下标编个index,从1开始; 模拟原始线段树的原始数组中给每个元素添加下标的逻辑
       /* for(Iterator iterator = pos.iterator(); iterator.hasNext();) {
            int key = (int) iterator.next();
            map.put(key, index++);
        }*/
        for (Integer key : pos) {
            map.put(key, index++);
        }
        return map;
    }

    public List<Integer> fallingSquares(int[][] positions)
    {
        //获取到了X轴上对应的下标
        HashMap<Integer, Integer> map = index(positions);
        int size = map.size();
        SegmentTree segmentTree = new SegmentTree(size);

        int left = 1;
        int right = size;
        int curIndex = 1;

        List<Integer> list = new ArrayList<>();

        for (int i = 0; i < positions.length; i++) {
            //任务开始下标
            int startIndex = map.get(positions[i][0]);
            //修改的值
            int value = positions[i][1];
            //任务结束下标; 此下标代表 [1,4) 替换成 [1,3]
            int endIndex = map.get(positions[i][0] + value - 1);

            //这个地方比较有意思,假如3-4区域高度max为5.
            //再降落一块高度为3的石头在1-3区间。不考虑重力因素
            //1-3的高度应该为 5 + 3 = 8; 哪怕之前1-2区域高度为0
            int ans = segmentTree.query(left, right, curIndex, startIndex, endIndex);
            int height = ans + value;
            //降落一块方块
            segmentTree.add(left, right, curIndex, startIndex, endIndex, height);
            //全区间查找最大值
            System.out.println(segmentTree.getTreeMaxHeight());
            list.add(segmentTree.getTreeMaxHeight());
        }
        return list;
    }

    public static void main(String[] args) {
        Code02_FallingSquares ss = new Code02_FallingSquares();
        //输出2 5 5
        int[][] positions = {{1,2},{2,3},{6,1}};
        ss.fallingSquares(positions);
/*
        int[][] positions2 = {{100,100},{200,100}};
        ss.fallingSquares(positions2);

        int[][] positions3 = {{9,7},{1,9},{3,1}};
        ss.fallingSquares(positions3);

        int[][] positions4 = {{6,4},{2,7},{6,9}};
        ss.fallingSquares(positions4);*/
    }
}

一顿操作猛如虎,结果测试发现胜率不到10%;

查了好久代码,没有发现什么结构上的问题。最终只注释掉了一行:

System.out.println(segmentTree.getTreeMaxHeight());

再测试发现, 96%的胜率:

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

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

相关文章

【蓝桥杯冲冲冲】[NOIP2001 普及组] 装箱问题

蓝桥杯备赛 | 洛谷做题打卡day26 文章目录 蓝桥杯备赛 | 洛谷做题打卡day26题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示思路 题解代码我的一些话 [NOIP2001 普及组] 装箱问题 题目描述 有一个箱子容量为 V V V&#xff0c;同时有 n n n 个物品&#xff0c;每…

jvm基础篇之垃圾回收[2](垃圾回收算法)

文章目录 版权声明垃圾回收算法核心思想垃圾回收算法的历史垃圾回收算法的评价标准垃圾分类算法分类标记清除算法核心思想标记清除算法优缺点 复制算法核心思想完整案例复制算法的优缺点 标记整理算法核心思想标记整理算法优缺点 分代垃圾回收算法arthas查看分代内存情况核心思…

【android】对于google-webrtc的性能中, memory leak

目录 zlmediakit->webrtcplay->app webrtcutil1/3 测试程序等 zlmediakit->webrtcplay->app 编译sdk 32 有时候会从开始新增5M&#xff0c;就稳定在一个值了 webrtcutil1/3 测试程序等 编译sdk 30

关于可变类型和不可变类型的探究

个人猜想&#xff08;很遗憾失败了&#xff09; 在硬盘或者系统中存在一个字符集 如果存在硬盘中&#xff0c;那么硬盘出厂的时候他的字符集所占用的空间就已经确定了。 如果存在于系统的话&#xff0c;硬盘应该在出厂的时候为系统设置一个存储系统字符集的地方。在安装系统…

【Chrono Engine学习总结】2-可视化

由于Chrono的官方教程在一些细节方面解释的并不清楚&#xff0c;自己做了一些尝试&#xff0c;做学习总结。 0、基本概念 类型说明&#xff1a; Chrono的可视化包括两块&#xff1a;实时可视化&#xff0c;以及离线/后处理可视化。 其中&#xff0c;实时可视化&#xff0c;又…

蓝桥杯省赛无忧 背包问题 课件 课件61 多重背包

01 多重背包基础模型 02 小明的背包3 03 二进制优化模型 04 新一的宝藏搜寻加强版

Linux Rootkit实验|0201 基本功能之Root后门

Linux Rootkit实验&#xff5c;0201 基本功能之Root后门 11 May 2017 文章目录 Linux Rootkit实验&#xff5c;0201 基本功能之Root后门实验说明实验环境实验过程提供 root 后门 实验总结与思考参考资料参考资料 时人不识凌云木&#xff0c;直待凌云始道高。 实验说明 本次实…

压力测试工具-Jmeter使用总结

目录 一.前言 二.线程组 三.线程组的组件 四.线程组-HTTP请求 1、JSON提取器 2、XPATH提取器 3、正则表达式提取器 五.线程组-断言 1、响应断言 2、JSON断言 六.创建测试 1.创建线程组 2.配置元件 3.构造HTTP请求 4.添加HTTP请求头 5.添加断言 6.添加查看结果树…

SAFe大规模敏捷认证Leading SAFe官方认证班

课程简介 SAFe – Scaled Agile Framework是目前全球运用最广泛的大规模敏捷框架&#xff0c;也是全球敏捷相关认证成长最快、最被认可、最有价值的规模化敏捷认证&#xff0c;目前全球SAFe认证专业人士已达120万人。 据官方统计&#xff0c;获得新证书的IT专业人士的平均工资…

寒假作业2月2号

第一章 命名空间 一&#xff0e;选择题 1、编写C程序一般需经过的几个步骤依次是&#xff08;C &#xff09; A. 编辑、调试、编译、连接 B. 编辑、编译、连接、运行 C. 编译、调试、编辑、连接 D. 编译、编辑、连接、运行 2、所谓数据封装就是将一组数据和与这组数据有关…

Pycharm python用matplotlib 3D绘图显示空白解决办法

问题原因&#xff1a; matplotlib版本升级之后显示代码变了&#xff0c;修改为新的 # ax Axes3D(fig) # 原代码 ax fig.add_axes(Axes3D(fig)) # 新代码import numpy as np import matplotlib.pyplot as plt from matplotlib import cm from mpl_toolkits.mplot3d import Ax…

Electron开发的十大神级产品,vscode、atom、skype、figma等

Hi、我是贝格前端工场&#xff0c;今天分享一下基于Electron的十大著名产品&#xff0c;欢迎友友们补充。 Visual Studio Code 这是一款由微软开发的轻量级代码编辑器&#xff0c;它提供了丰富的功能和插件生态系统&#xff0c;支持多种编程语言和开发工具。Visual Studio Cod…

PDF中公式转word

效果&#xff1a;实现pdf中公式免编辑 step1: 截图CtrlAltA&#xff0c;复制 step2: SimpleTex - Snip & Get 网页或客户端均可&#xff0c;无次数限制&#xff0c;效果还不错。还支持手写、文字识别 单张图片&#xff1a;选 手写板 step3: 导出结果选择 注&#xff1a;…

【笔记】Android 常用编译模块和输出产物路径

模块&产物路径 具体编译到软件的路径要看编译规则的分区&#xff0c;代码中模块编译输出的产物基本对应。 Android 代码模块 编译产物路径设备adb路径Comment 模块device/mediatek/system/common/ 资源overlay/telephony/frameworks/base/core 文件举例res/res/values-m…

开源的三维算法库有哪些

PCL,VTK,VCG,CGAL&#xff0c;Open CASCADE&#xff08;opencascade&#xff09;&#xff0c;OpenSceneGraph (OSG)&#xff0c; 点云网格处理算法&#xff1a;openmesh, meshlab三维算法库&#xff0c;Eigen 网格简化&#xff0c;网格平滑&#xff0c;网格参数化 无序点云网…

[Linux 进程(六)] 写时拷贝 - 进程终止

文章目录 1、写时拷贝2、进程终止2.1 进程退出场景2.1.1 退出码2.1.2 错误码错误码 vs 退出码2.1.3 代码异常终止引入 2.2 进程常见退出方法2.2.1 exit函数2.2.2 _exit函数 本片我们主要来讲进程控制&#xff0c;讲之前我们先把写时拷贝理清&#xff0c;然后再开始讲进程控制。…

与指定数字相同的数的个数 T1061

#include<bits/stdc.h> using namespace std; int n,m; const int N110; int f[N]; int a0; int main(){cin>>n>>m;for(int i0;i<n;i)cin>>f[i];for(int j0;j<n;j){if(f[j]m){a;}}cout<<a<<endl;return 0; }

安科瑞消防设备电源监控系统在地铁工程的设计与应用

【摘要】&#xff1a;本文介绍了地铁工程中消防设备电源监控系统设置的必要性及规范求&#xff0c;分析了监控设计方案&#xff0c;提出该系统在地铁工程中的应用要求及建议&#xff0c;以供地铁工程建设参考。消防设备电源监控系统主要针对消防用电设备的电源进行实时的监控&a…

react 之 zustand

zustand可以说是redux的平替 官网地址&#xff1a;https://zustand-demo.pmnd.rs/ 1.安装 npm i zustand2.基础使用 // zustand import { create } from zustand// 1. 创建store // 语法容易出错 // 1. 函数参数必须返回一个对象 对象内部编写状态数据和方法 // 2. set是用来…

Springboot做查询数据库某个表的数据时,后台一切正常前台显示不了数据

当我在用springboot做项目的时候查询整个表的数据或者条件查询的时候发现我的后台功能一切正常但是我的前台界面就是显示不了数据&#xff0c;这个问题解决也很简单&#xff0c;就是需要我们平时多加注意&#xff0c;不要漏代码&#xff01;&#xff01;&#xff01; Builder …