刷题笔记Day30贪心算法part04

刷题笔记Day30:贪心算法part04

题目:用最少数量的箭引爆气球

452. 用最少数量的箭引爆气球 - 力扣(LeetCode)

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

一支弓箭可以沿着 x 轴从不同点 完全垂直 地射出。在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 x``startx``end, 且满足 xstart ≤ 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]。

思路:是一个重叠问题,因此需要先对points做一个排序结果为[[1,6],[2,8],[7,12],[10,16]],经过排序后我们要对比的方式是此刻气球的开始位置和上一个气球的结束位置是否有重叠,若有重叠说明可以一次打爆两个气球,例如第一个和第二个,但是如何判断第三个或者以后的气球是否在第一支箭矢的,因此在第一次判断后若能使用一支箭射爆两个气球后,需要判断第一个气球的末尾位置和第二个气球的末尾位置哪个位置较小,将较小的值赋给第二个气球的末尾,一次类推。代码:

class Solution {
public:
    static bool cmp(const vector<int>& a, const vector<int>& b){
        if(a[0] == b[0])    return a[1]<b[1];
        return a[0]<b[0];
    }
    int findMinArrowShots(vector<vector<int>>& points) {
        int result = 1;
        sort(points.begin(),points.end(),cmp);
        for(int i = 1; i<points.size();i++){
            if(points[i][0] <= points[i-1][1]){
                points[i][1] = min(points[i][1],points[i-1][1]);
            }
            else
            {
                result++;
            }
        }
        return result;
    }
};

题目:无重叠区间

435. 无重叠区间 - 力扣(LeetCode)

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

注意 只在一点上接触的区间是 不重叠的。例如 [1, 2][2, 3] 是不重叠的。

示例 1:

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

思路:和上面那道题类似,遍历的套路相同,但是result++的意思不同,在本体中result++的意思是将其余重叠的所有区间删除,而intervals[i][1] = min(intervals[i][1],intervals[i-1][1]);的意思是保留重叠区间内的最小区间用于和下一个区间进行对比。

class Solution {
public:
    static bool cmp(const vector<int>& a, const vector<int>& b){
        if(a[0] == b[0])    return a[1]<b[1];
        return a[0]<b[0];
    }
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        sort(intervals.begin(),intervals.end(),cmp);
        int result = 0;
        for(int i = 1; i < intervals.size();i++){
            if(intervals[i][0] < intervals[i-1][1]){
                result++;
                intervals[i][1] = min(intervals[i][1],intervals[i-1][1]); //这个操作相当于保留有边界最小的那个,上面的result++相当于将其余重叠的所有区间删除。
            }
            else{
                ;
            }
        }
        return result;
    }
};

题目:划分字母区间

763. 划分字母区间 - 力扣(LeetCode)

给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。例如,字符串 "ababcc" 能够被分为 ["abab", "cc"],但类似 ["aba", "bcc"]["ab", "ab", "cc"] 的划分是非法的。

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

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

示例 1:

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

思路:本题还是一个区间覆盖的问题,例如示例1中的区间覆盖可以表示为存在a的起始位置和最远位置的区间。但本体我们只需要用到右区间即可,即元素所在的最大位置,例如下图为示例1中的ababcbaca这个子区间,可以发现这个子区间内元素的所在的最大位置就是a元素的8,因此这么切是合理的,符合题意的,后面的模拟过程以此类推。具体代码如下:

image-20250424132845787

class Solution {
public:
    vector<int> partitionLabels(string s) {
        int hash[27] = {0};
        for(int i = 0; i < s.size();i++){
            hash[s[i]-'a'] = i; 
        }
        int left =0;
        int right = 0;
        vector<int> result;

        for(int i=0;i<s.size();i++){
            right = max(right , hash[s[i]-'a']);
            if(right == i)
            {
                result.push_back(right - left + 1);
                left = i+1;
            }
        }
        return result;
    }
};
posted @ 2025-04-24 13:31  涛Tao  阅读(76)  评论(0)    收藏  举报