Leetcode 435. Non-overlapping Intervals/452. Minimum Number of Arrows to Burst Balloons

这两个题目的思路都是一样的. 都是在经典的任务安排问题上变种过来的. 先看435题:

给一堆的区间, 其中有的区间之间是相互重叠的. 我们需要删除其中的一些区间. 使得剩下的区间之间没有重叠 (注意: [1, 2], [2,3]两区间并没有重叠, 只是紧邻). 要求找到最少删除数.

Input: [ [1,2], [2,3], [3,4], [1,3] ]

Output: 1

我们只需要删除[1, 3]区间就可以了

这个问题的主体思路是贪心算法. 最少删除也就对应着最多的保留量. 所以我们找一个方案尽量多地保留区间.

我们每次从剩余区间中取最早结束的那个区间将它保留下来, 其结束时间记为 ei , 其他区间V[k]如果开始时间大于 ei , 那么V[k]只能被删除.

a证明: 数学归纳法.
就假设用我们的算法得到的解是A {a1,a2,...,am} , 最佳解是O {b1,b2,...,bn} , 我们将证明m=n:

记题目给的区间总数为N.
1. 当n=1的时候表示最佳方案只挑选了第一个区间, m= 1, n= 1.显然成立. aend1bend1
2. 假如当n=i-1的时候命题成立, 即A集合的大小等于O集合的大小. 我们要证明n=i的时候命题也成立, :因为 aendi1bendi1,bendi1bstarti 根据我们上面的算法, 每次往A里面新添加的都是剩余的最早结束区间, 所以 aendibendi .

综上我们可以得到 ambn 的, 所以处理完所有的区间后, A的大小一定是大于等于O的大小的. 所以A就是最佳解.

   int eraseOverlapIntervals(vector<Interval>& intervals) {
       if(0 == intervals.size()) {
           return 0;
       }
       auto cmp_end = [](const Interval & l, const Interval & r) {
           return l.end < r.end;
       };
       std::sort(intervals.begin(), intervals.end(), cmp_end);

       int e = intervals[0].end;
       int cnt = 1;
       for(size_t i = 1; i < intervals.size(); ++i) {
           if(intervals[i].start < e) {
               continue;
           }

           e = intervals[i].end;
           ++cnt;
       }
       return intervals.size() - cnt;
   }

在实现的时候有个小技巧. 按照end排序之后. 依次按照end从低到高遍历区间. 记录上一个被选择的区间的end值. 如果现在的这个区间的start小于prev_end. 说明这个区间早就应该被删除的. 所以丢弃这个区间. 这样我们每次添加一个区间之后, 并不需要再去找与它重叠的区间.


leetcode 452的打气球问题也是一样的思路. 每个气球最后都需要被打爆, 我们需要尽量做到”一石二鸟”, “一石N鸟”. 每次从剩余的气球中找end最小的那个, 打爆这个气球的朝着它的end的位置发射, 这样能尽可能地多带几个其他的气球一起爆掉.

int findMinArrowShots(vector<pair<int, int>>& points) {
    using Point=pair<int, int>;
    const int sz = points.size();
    if(0 == sz) {
        return 0;
    }

    auto cmp_end = [](const Point & l, const Point & r) {
        return l.second< r.second;
    };
    sort(points.begin(), points.end(), cmp_end);

    int cnt = 1;
    int prev_e = points[0].second;
    for(int i = 1; i < sz; ++i) {
        if(points[i].first<= prev_e) {
            continue;
        }
        prev_e = points[i].second;
        ++cnt;
    }

    return cnt;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值