用最少数量的箭引爆气球[中等]

优质博文:IT-BLOG-CN

一、题目

有一些球形气球贴在一堵用XY平面表示的墙面上。墙面上的气球记录在整数数组points,其中points[i] = [xstart, xend]表示水平直径在xstartxend之间的气球。你不知道气球的确切y坐标。一支弓箭可以沿着x轴从不同点完全垂直地射出。在坐标x处射出一支箭,若有一个气球的直径的开始和结束坐标为xstart,xend,且满足xstart ≤ x ≤ xend,则该气球会被引爆 。可以射出的弓箭的数量没有限制。弓箭一旦被射出之后,可以无限地前进。

给你一个数组points,返回引爆所有气球所必须射出的最小弓箭数。

示例 1:
输入:points = [[10,16],[2,8],[1,6],[7,12]]
输出:2
解释:气球可以用2支箭来爆破:
-x = 6处射出箭,击破气球[2,8][1,6]
-x = 11处发射箭,击破气球[10,16][7,12]

示例 2:
输入:points = [[1,2],[3,4],[5,6],[7,8]]
输出:4
解释:每个气球需要射出一支箭,总共需要4支箭。

示例 3:
输入:points = [[1,2],[2,3],[3,4],[4,5]]
输出:2
解释:气球可以用2支箭来爆破:
-x = 2处发射箭,击破气球[1,2][2,3]
-x = 4处射出箭,击破气球[3,4][4,5]

1 <= points.length <= 105
points[i].length == 2
-231 <= xstart < xend <= 231 - 1

二、代码

排序 + 贪心: 我们首先随机地射出一支箭,再看一看是否能够调整这支箭地射出位置,使得我们可以引爆更多数目的气球。

如图1-1所示,我们随机射出一支箭,引爆了除红色气球以外的所有气球。我们称所有引爆的气球为「原本引爆的气球」,其余的气球为「原本完好的气球」。可以发现,如果我们将这支箭的射出位置稍微往右移动一点,那么我们就有机会引爆红色气球,如图1-2所示。那么我们最远可以将这支箭往右移动多远呢?我们唯一的要求就是:原本引爆的气球只要仍然被引爆就行了。这样一来,我们找出原本引爆的气球中右边界位置最靠左的那一个,将这支箭的射出位置移动到这个右边界位置,这也是最远可以往右移动到的位置:如图1-3所示,只要我们再往右移动一点点,这个气球就无法被引爆了。

为什么「原本引爆的气球仍然被引爆」是唯一的要求?别急,往下看就能看到其精妙所在。

因此,我们可以断定:一定存在一种最优(射出的箭数最小)的方法,使得每一支箭的射出位置都恰好对应着某一个气球的右边界。

这是为什么?我们考虑任意一种最优的方法,对于其中的任意一支箭,我们都通过上面描述的方法,将这支箭的位置移动到它对应的「原本引爆的气球中最靠左的右边界位置」,那么这些原本引爆的气球仍然被引爆。这样一来,所有的气球仍然都会被引爆,并且每一支箭的射出位置都恰好位于某一个气球的右边界了。

有了这样一个有用的断定,我们就可以快速得到一种最优的方法了。考虑所有气球中右边界位置最靠左的那一个,那么一定有一支箭的射出位置就是它的右边界(否则就没有箭可以将其引爆了)。当我们确定了一支箭之后,我们就可以将这支箭引爆的所有气球移除,并从剩下未被引爆的气球中,再选择右边界位置最靠左的那一个,确定下一支箭,直到所有的气球都被引爆。

我们可以写出如下的伪代码:

let points := [[x(0), y(0)], [x(1), y(1)], ... [x(n-1), y(n-1)]],表示 n 个气球
let burst := [false] * n,表示每个气球是否被引爆
let ans := 1,表示射出的箭数

将 points 按照 y 值(右边界)进行升序排序

while burst 中还有 falsedo
    let i := 最小的满足 burst[i] = false 的索引 i
    for j := i to n-1 do
        if x(j) <= y(i) then
            burst[j] := true
        end if
    end for
end while

return ans

这样的做法在最坏情况下时间复杂度是O(n^2),即这n个气球对应的区间互不重叠,while循环需要执行n次。那么我们如何继续进行优化呢?

事实上,在内层的j循环中,当我们遇到第一个不满足x(j)≤y(i)j值,就可以直接跳出循环,并且这个y(j)就是下一支箭的射出位置。为什么这样做是对的呢?我们考虑某一支箭的索引it以及它的下一支箭的索引jt​,对于索引在jt之后的任意一个可以被it引爆的气球,记索引为j0​,有:x(j0)≤y(it)由于y(it)≤y(jt)显然成立,那么x(j0)≤y(jt)也成立,也就是说:当前这支箭在索引jt​(第一个无法引爆的气球)之后所有可以引爆的气球,下一支箭也都可以引爆。因此我们就证明了其正确性,也就可以写出如下的伪代码:

let points := [[x(0), y(0)], [x(1), y(1)], ... [x(n-1), y(n-1)]],表示 n 个气球
let pos := y(0),表示当前箭的射出位置
let ans := 1,表示射出的箭数

将 points 按照 y 值(右边界)进行升序排序

for i := 1 to n-1 do
    if x(i) > pos then
        ans := ans + 1
        pos := y(i)
    end if
end for

return ans

这样就可以将计算答案的时间从O(n^2)降低至O(n)

class Solution {
    public int findMinArrowShots(int[][] points) {
        if (points.length == 0) {
            return 0;
        }
        Arrays.sort(points, new Comparator<int[]>() {
            public int compare(int[] point1, int[] point2) {
                if (point1[1] > point2[1]) {
                    return 1;
                } else if (point1[1] < point2[1]) {
                    return -1;
                } else {
                    return 0;
                }
            }
        });
        int pos = points[0][1];
        int ans = 1;
        for (int[] balloon: points) {
            if (balloon[0] > pos) {
                pos = balloon[1];
                ++ans;
            }
        }
        return ans;
    }
}

时间复杂度: O(nlog⁡n),其中n是数组points的长度。排序的时间复杂度为O(nlog⁡n),对所有气球进行遍历并计算答案的时间复杂度为O(n),其在渐进意义下小于前者,因此可以忽略。
空间复杂度: O(log⁡n),即为排序需要使用的栈空间。

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

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

相关文章

Springboot-注册注解【springboot常用注解】

1.组件注册 1.1 使用的注解 Configuration:普通配置类,替代以前的配置文件,配置类本身也是容器的组件|SpringBootConfiguration:Springboot配置类,与Configuration功能一样|Bean:替代以前的Bean标签,如果没有在Bean标签内定义名字,则默认组件的名字为方法名,可以直接修改注解…

在gitlab上使用server_hooks

文章目录 1. 前置条件2. Git Hook2.1 Git Hook 分为两部分&#xff1a;本地和远程2.1.1 本地 Git Hook&#xff0c;由提交和合并等操作触发&#xff1a;2.1.2 远程 Git Hook&#xff0c;运行在网络操作上&#xff0c;例如接收推送的提交&#xff1a; 3. 操作步骤3.1 对所有的仓…

Linux下的文件IO之系统IO

1. 知识点 读入写出&#xff0c;切记以我们程序为中心向文件或者别的什么东西读入写出&#xff08;输入流输出流&#xff09; 人话就是 文件向我们程序就是读入 程序向文件或者别的什么就是写出 2. open打开文件 open.c /****************************************************…

C++ 学习之函数成员指针的一个小细节

看看下面的代码&#xff0c;你能看出错误吗 class A { public:void fun(){}}; int main() {A a;void (A:: * p)() &A::fun;(*p)(); } 这段代码在调用成员函数时存在问题。正确的方式是使用对象来调用成员函数&#xff0c;而不是通过指针。以下是修正后的代码&#xff1a…

STM32CubeIDE(CUBE-MX hal库)----定时器

系列文章目录 STM32CubeIDE(CUBE-MX hal库)----初尝点亮小灯 STM32CubeIDE(CUBE-MX hal库)----按键控制 STM32CubeIDE(CUBE-MX hal库)----串口通信 文章目录 系列文章目录前言一、定时器二、使用步骤三、HAL库实验代码三、标准库代码 前言 STM32定时器是一种多功能外设&#…

异常 Exception 02

异常 Exception 02 六、异常处理1、基本介绍2、异常处理的方式3、示意图 try-catchthrows1、介绍2、注意事项 自定义异常1、基本概念2、自定义异常的步骤3、实例4、throw和throws的区别 六、异常处理 1、基本介绍 异常处理就是当异常发生时,对异常处理的方式。 2、异常处理的…

以STM32CubeMX创建DSP库工程方法一

以STM32CubeMX创建DSP库工程方法 略过时钟树的分配和UART的创建等&#xff0c;直接进入主题生成工程文件 它们中的文件功能如下&#xff1a; 1&#xff09;BasicMathFunctions 基本数学函数&#xff1a;提供浮点数的各种基本运算函数&#xff0c;如向量加减乘除等运算。 2&…

VBA代码解决方案第8讲:用FindPrevious进行重复搜索及利用LIKE查找

《VBA代码解决方案》(版权10028096)这套教程是我最早推出的教程&#xff0c;目前已经是第三版修订了。这套教程定位于入门后的提高&#xff0c;在学习这套教程过程中&#xff0c;侧重点是要理解及掌握我的“积木编程”思想。要灵活运用教程中的实例像搭积木一样把自己喜欢的代码…

货代FOB条款卖方必备的知识:发货人都要承担哪些费用呢?

据统计&#xff0c;中国出口中以FOB成交的占到70%&#xff0c;但专家指出&#xff1a;FOB对出口商的风险更大&#xff0c;有可能造成货、款两空的结局。 目前我国出口合同以FOB价格条款成交的比例越来越大&#xff0c;而且收货人指定船公司的少&#xff0c;指定境外货代的多&am…

3款厉害的小工具,小黑子都在用!

大家好&#xff0c;我是 Javapub。 程序员与普通人最大的区别是什么&#xff0c;当然是会使用工具。基于一些同学经常问我的问题&#xff0c;接下来给大家分享几款我经常使用的工具&#xff0c;主打一个提升效率。 第一款 Everything 用 windwos 的同学都体会过&#xff0c;…

带大家做一个,易上手的家常土豆片

还是先从冰箱里那一块猪瘦肉 搞一点蒜和生姜 切成小块 装进一个碗里 这里一点就够了 一条绿皮辣椒 切片 三个左右干辣椒 随便切两刀 让它小一点就好了 一起装一个碗 一大一小两个土豆切片 猪肉切片 起锅烧油 然后 下肉翻炒 等肉变颜色捞出来 然后放入土豆 和小半碗水 让…

JeecgBoot低代码开发—Vue3版前端入门教程

JeecgBoot低代码开发—Vue3版前端入门教程 后端接口配置VUE3 必备知识1.vue3新特性a. https://v3.cn.vuejs.org/b.setup的用法c.ref 和 reactive 的用法d.新版 v-model 的用法e.script setup的用法 2.TypeScript基础 后端接口配置 如何修改后台项目路径 http://127.168.3.52:8…

【玩转 EdgeOne】| 腾讯云下一代边缘加速CDN EdgeOne 是安全加速界的未来吗?

目录 前言边缘加速与安全加固边缘计算与CDN的融合EdgeOne优秀的安全特性EdgeOne卓越的性能表现灵活的配置和管理生态系统的支持与发展技术创新与未来展望EdgeOne试用结束语 前言 在当下互联网的迅猛发展的时刻&#xff0c;云计算和边缘计算技术的快速发展为网络加速领域带来了…

awk从放弃到入门(10):awk内置函数

awk从放弃到入门&#xff08;10&#xff09;&#xff1a;awk内置函数 算数函数字符串函数其他函数 本博文转载自 这篇文章中的知识点是建立在前文的基础上的&#xff0c;如果你还没有掌握前文中的知识&#xff0c;请先参考之前的文章。 注&#xff1a;在阅读这篇文章之前&…

Abbyy FineReader16最新版本有哪些新功能?

在数字化时代&#xff0c;数据处理和转换变得非常重要&#xff0c;Abbyy FineReader 就是一款专门用于处理、转换和识别图像和 PDF 文件的软件。在本文中&#xff0c;我们将会详细介绍 Abbyy FineReader 的功能以及适合使用该软件的电脑。 ABBYY Finereader 16-安装包下载如下&…

滴滴2023.11.27P0级故障技术复盘回顾(k8s的的错?)

本文从滴滴官方恢复及技术公众号带大家从技术角度复盘这次事故 目录 1. 背景 2. 滴滴官方消息 3. 问题分析及定位 4.网传的k8s及解析 5.k8s引发的思考&#xff1a;举一反三&#xff0c;怎么避免再次出现 6.近段时间其他平台崩溃回顾 1. 背景 11 月 27 晚约 10 点&#xf…

TFIDF、BM25、编辑距离、倒排索引

TFIDF TF刻画了词语t对某篇文档的重要性&#xff0c;IDF刻画了词语t对整个文档集的重要性

最简单的链路追踪收集器

链路追踪可帮助您快速了解程序服务之间的调用关系&#xff0c;并快速洞悉内部发生的情况。主流的链路追踪系统有zipkin,jaeger,skywalking等&#xff0c;由于opentelemetry的存在&#xff0c;都具有opentelemetry的转换器。 我们利用opentelemetry来进行zipkin,jaeger,skywalk…

智能优化算法应用:基于生物地理学算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于生物地理学算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于生物地理学算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.生物地理学算法4.实验参数设定5.算法结果…

在Rust中编写自动化测试

1.摘要 Rust中的测试函数是用来验证非测试代码是否是按照期望的方式运行的, 测试函数体通常需要执行三种操作:1.设置任何所需的数据或状态;2.运行需要测试的代码;3.断言其结果是我们所期望的。本篇文章主要探讨了Rust自动化测试的几种常见场景。 2.测试函数详解 在Rust项目工…