24暑假算法刷题 | Day30 | 贪心算法 IV | 452. 用最少数量的箭引爆气球,435. 无重叠区间,763. 划分字母区间

目录


452. 用最少数量的箭引爆气球

点此跳转题目链接

题目描述

有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points ,其中points[i] = [x_start, x_end] 表示水平直径在 x_start 和 x_end之间的气球。你不知道气球的确切 y 坐标。

一支弓箭可以沿着 x 轴从不同点 完全垂直 地射出。在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 x_start,x_end, 且满足 x_start ≤ x ≤ x_end,则该气球会被 引爆 。可以射出的弓箭的数量 没有限制 。 弓箭一旦被射出之后,可以无限地前进。

给你一个数组 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

题解

贪心算法解决。先按照气球的左边界从小到大(按右边界同理)对 points 进行排序。然后,符合直觉地,应该依次尝试将下一个气球"纳入"当前的"命中范围",从而将所有气球分成连续紧挨着的若干组,每组用一只箭即可"贯穿"。

这个"命中范围"的左边界就是当前气球左边界(最大的),右边界就是目前这组气球右边界的最小值。如果新的气球的左边界已经大于之前一组气球的右边界,即它肯定会与前一组气球的命中范围"错开",则开始新一组。

代码(C++)

cpp 复制代码
int findMinArrowShots(vector<vector<int>> &points)
{
    auto cmp = [](const vector<int> &a, const vector<int> &b) {return a[0] < b[0];};
    sort(points.begin(), points.end(), cmp); // 先对points排序
    int count = 1;
    int curMin = points[0][0], curMax = points[0][1];
    for (int i = 1; i < points.size(); ++i) {
        if (points[i][0] > curMax) { // 需要开始新一组
            curMin = points[i][0], curMax = points[i][1];
            count++;
        } else 
            curMin = points[i][0], curMax = min(curMax, points[i][1]); // 更新左右边界
    }
    return count;
}

435. 无重叠区间

点此跳转题目链接

题目描述

给定一个区间的集合 intervals ,其中 intervals[i] = [starti, endi] 。返回 需要移除区间的最小数量,使剩余区间互不重叠 。

示例 1:

复制代码
输入: intervals = [[1,2],[2,3],[3,4],[1,3]]
输出: 1
解释: 移除 [1,3] 后,剩下的区间没有重叠。

示例 2:

复制代码
输入: intervals = [ [1,2], [1,2], [1,2] ]
输出: 2
解释: 你需要移除两个 [1,2] 来使剩下的区间没有重叠。

示例 3:

复制代码
输入: intervals = [ [1,2], [2,3] ]
输出: 0
解释: 你不需要移除任何区间,因为它们已经是无重叠的了。

提示:

  • 1 <= intervals.length <= 105
  • intervals[i].length == 2
  • -5 * 104 <= starti < endi <= 5 * 104

题解

贪心算法解决。这题和 452. 用最少数量的箭引爆气球 蛮像的,因为要" 去除 重叠区间"自然得先" 找到 重叠区间"。还是先对原区间集合按照左边界从小到大(按右边界同理)排序,然后"贪心"地在顺序遍历过程中找到那些"互相重叠"的区间。

这里的"互相重叠"指的是两两之间都重叠,例如:

上面每条线段表示一个区间。可以看出,任意两线段(区间)之间都有重叠区域,即这三个区间互相重叠。而如果是下面这种情况:

第1、2个区间有重叠,第2、3个区间有重叠,但是1、3区间无重叠,则这仨不是"互相重叠"。

然后,每组"互相重叠"的区间最终应该只能保留其中某一个区间,从而组成最后的无重叠区间。因此,找到了互相重叠区间的数量,也就等于找到了最终无重叠区间的数量,那么用原区间的数量减去它,自然就是要移除区间的最小数量了。

代码(C++)

cpp 复制代码
int eraseOverlapIntervals(vector<vector<int>> &intervals)
{
    if (intervals.size() == 1)
        return 0;
    
    auto cmp = [](const vector<int> &a, const vector<int> &b) {
        return a[0] < b[0];
    };
    sort(intervals.begin(), intervals.end(), cmp);
    
    int iCount = 1; // 最终的无重叠区间数量
    int curMinRight = intervals[0][1];
    for (int i = 1; i < intervals.size(); ++i) {
        if (intervals[i][0] >= curMinRight) {
            iCount++;
            curMinRight = intervals[i][1];
        } else 
            curMinRight = min(intervals[i][1], curMinRight);
    }
    return intervals.size() - iCount;
}

763. 划分字母区间

点此跳转题目链接

题目描述

给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。

注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s 。

返回一个表示每个字符串片段的长度的列表。

示例 1:

复制代码
输入:s = "ababcbacadefegdehijhklij"
输出:[9,7,8]
解释:
划分结果为 "ababcbaca"、"defegde"、"hijhklij" 。
每个字母最多出现在一个片段中。
像 "ababcbacadefegde", "hijhklij" 这样的划分是错误的,因为划分的片段数较少。 

示例 2:

复制代码
输入:s = "eccbbbbdec"
输出:[10]

提示:

  • 1 <= s.length <= 500
  • s 仅由小写英文字母组成

题解

根据题目要求,我们不难快速想到一个符合直觉的算法:在某个字母最后一次出现的地方分割,同时保证分割后的这一段中,其他字母也仅在这一段出现。

每个字母最后出现的位置容易解决,遍历一遍字符串、不断更新出现位置即可。而要保证分割后,其他字母也只在这段出现,则说明分割处的字母是这段子串里所有字母中,"最后出现位置"最靠后的的一个。所以我们只需要维护一个"当前出现的最后位置的最大值"即可,借用 代码随想录 中的图可以直观说明:

代码(C++)

cpp 复制代码
vector<int> partitionLabels(string s)
{
    // 找到每个字母最后一次出现的位置
    unordered_map<char, int> lastOccur;
    for (int i = 0; i < s.size(); ++i)
        lastOccur[s[i]] = i;
    vector<int> res;
    int curCount = 0;
    int curLastPos = -1;
    for (int i = 0; i < s.size(); ++i) {
        curLastPos = max(curLastPos, lastOccur[s[i]]);
        curCount++;
        if (curLastPos == i) {
            res.push_back(curCount);
            curCount = 0;
        }   
    }
    return res;
}
相关推荐
旺仔仔仔39 分钟前
机器人项目实战(5):从单次抓取,到接收指令连续做事
算法
旺仔仔仔1 小时前
机器人项目实战(2):识别物体、提取点云,再到抓取姿态
算法
姜鱼问生1 小时前
密码明明一样,为什么 GPG 说“错的“?
后端·算法·架构
启观川1 小时前
数据结构与算法-第 3 章 常用算法-贪心算法
数据结构·笔记·python·算法·贪心算法
对空六课1 小时前
指标口径生效时间的版本查询实现
大数据·服务器·前端·算法·数据分析
对空六课1 小时前
RFM分层边界变化的回溯计算
大数据·服务器·前端·数据库·人工智能·算法·数据分析
一勺思维1 小时前
我为什么把 Agent 的核心逻辑从 LangChain 里搬出来手写
算法
碧海银沙音频科技研究院2 小时前
基于炬芯ATS308X的LE aduio LC3编解码算法功能实现
算法
小蒋观天下2 小时前
厂区人员徘徊游荡判别——基于时序轨迹速度的逗留行为分级算法落地
大数据·人工智能·深度学习·算法·机器学习·ai大模型
程序员清风2 小时前
堆与优先队列:从 Top K 到任务调度
java·数据结构·算法