当前位置: 首页 > news >正文

day30-贪心__452. 用最少数量的箭引爆气球__435. 无重叠区间__763.划分字母区间

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

对于这道题来说,初看的时候,我们会认为复杂繁琐,但是如果我们对每一个vector 按照左端点或者按照右端点进行排序之后,我们去射击气球时,每次射击都尽可能的设计区间的重叠部分,这样就能保证我们花费的箭最少。那么如何判断气球之间是否发生重合呢

根据题目示例的[[10,16],[2,8],[1,6],[7,12]],我们将其按照左端点从小到大能画出这样的图,那只要后一段的左端点小于前一段的右端点时,这两段就发生了重叠。只需要一只箭,那重叠之后,我们就需要向后判断了,这个时候问题就来了我们判断第三段的时候,是使用第一段的右端点还是第二段的右端点来判断呢。显然我们这里应该选择的判断目标应该是min(第一段的右端点, 第二段的右端点), 这样才能达到,我们想要判断第三段和前两段是否同时重叠的目的!

有了上述的思考后,我们就很容易想到,对排序后的数组做一次遍历。每一次都去判断前一段的右端点,是否大于等于后一段的左端点。如果右端点大于左端点,更新新的右端点为min(前一段右端点, 后一段右端点),然后在继续循环。如果不满足条件,result++。

代码如下:

class Solution {
private:
    static bool cmp(const vector<int>& a, const vector<int>& b) {
        return a[0] < b[0];
    }
public:
    int findMinArrowShots(vector<vector<int>>& points) {
        if (points.size() == 0) return 0;
        sort(points.begin(), points.end(), cmp);

        int result = 1; // points 不为空至少需要一支箭
        for (int i = 1; i < points.size(); i++) {
            if (points[i][0] > points[i - 1][1]) {  // 气球i和气球i-1不挨着,注意这里不是>=
                result++; // 需要一支箭
            }
            else {  // 气球i和气球i-1挨着
                points[i][1] = min(points[i - 1][1], points[i][1]); // 更新重叠气球最小右边界
            }
        }
        return result;
    }
};

435. 无重叠区间

这道题其实和上一题很类似,像当于换了一个马甲。计算要移除的重叠区间个数。判断重叠区间的方法和上一题是一样的只是不包含(前一段右端点==后一段左端点)这种情况,所以当出现(前一段的右端点 < 后一段的左端点)时,我们判断出发生重叠。这个时候就要移除某一段区间,那么是移动第一段还是第二段呢? 这里其实我们要移除两端中右端点更小的哪一段,因为只有这样才能保证后一段(第三段)能够更小概率和这一段重叠。但是由于这道题,只需要记录移除的段数,所以我们不需要去对这个数组做移除操作,只需要将第二段的右端点更新为min(第一段右端点,第二段右端点)即可。

代码如下:

class Solution {
public:
    static bool cmp(vector<int> a,vector<int> b)
    {
        return a[0] < b[0];
    }
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
       sort(intervals.begin(),intervals.end(),cmp);
       int ans = 0;
       for(int i=1;i<intervals.size();i++)
       {
            if(intervals[i-1][1] > intervals[i][0])   // 发生重叠
            {
                intervals[i][1] = min(intervals[i][1], intervals[i-1][1]);
                ans++;
            }
       } 
       return ans;
    }
};

763.划分字母区间

这道题看起来相当的复杂,因为在我们动态计算最远划分距离的过程中,会不断添加新的字母进来,导致我们的动态划分距离要考虑的字母数越来越多,这显然是很复杂的。

但是我们不妨先对这个字符串,进行一次访问,记录出这个字符串中,每个字母的最远出现位置。(使用类似桶排序的方式)

代码如下:

int hash[27] = {0};
// 记录每个字母的最远出现位置
for(int i=0;i<s.size();i++)
{
    hash[s[i]-'a'] = i;
}

有了这个数组之后,我们可以记录一个right变量,并将其初始化为hash[s[0] - ‘a’],即字符串中0号位置元素的最远距离,然后开始遍历,没遍历一个元素就尝试去更新right,更新的方式为max(right, hash[s[i] - ‘a’]),即right永远保持为当前遍历过字母的最远出现位置。那我们什么时候记录一个答案呢? 当然是right == i 的时候,即我们已经走到了当前遍历过字母的最远位置,这时我们需要记录答案,而由于这道题要求我们保存的是每一段的元素个数,所以我们除了right之外,还需要一个left,用来记录我们开始时的左端点。

最终代码如下:

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;
        }
        vector<int> ans;
        int left = 0,right = 0;
        for(int i=0;i<s.size();i++)
        {
            right = max(right,hash[s[i] - 'a']);
            if(right == i)
            {
                 ans.push_back(right - left + 1);
                 left = i+1;
            }
        }
        return ans;
    }
};

相关文章:

  • 用Python修改字体字形与提取矢量数据:fontTools实战指南
  • 【数据分析实战】使用 Matplotlib 绘制折线图
  • 算法训练之动态规划(五)——简单多状态问题
  • 【辰辉创聚生物】提供上万种单抗/多抗及其偶联物
  • 程序加壳脱壳原理和实现
  • P3367 【模板】并查集
  • 【局域网】
  • 记 etcd 无法在docker-compose.yml启动后无法映射数据库目录的问题
  • 视觉目标检测大模型GAIA
  • HTTP:三.HTTP报文
  • Win11系统 VMware虚拟机 安装教程
  • mac|使用scrcpy实现无线Android投屏
  • android TabLayout中tabBackground和background的区别
  • SSRF打靶总结
  • JS 构造函数实现封装性
  • 从keys到SCAN:Redis批量删除的进化之路
  • BT面板docker搭建excalidraw遇到的问题
  • AI大模型从0到1记录学习 day16
  • 水利水电安全员C证怎么考,报考有什么流程
  • Franka 机器人x Dexterity Gen引领遥操作精细任务新时代
  • wordpress文本小工具栏/win7优化设置
  • 七台河哈尔滨网站建设/谷歌商店官网下载
  • 福州网站平台建设公司/西安网站优化
  • 德州网站seo/平台推广文案
  • 网站开发属于什么系统/google推广seo
  • 济南高端网站设计/怎样创建自己的电商平台