关于区间覆盖下,求最多的覆盖次数问题

题目描述:

今天又碰到之前似乎写过的一种题,区间覆盖中的一种比较简单的题型。

大意是给你n个区间段[l,r],某个点可能会被一些区间覆盖住,求最多的覆盖区间个数(n<=1e6,l,r<=1e9)

举几个题目例子:

1.你有一些任务从时间l开始,时间r结束,你需要招募一些员工去完成这些任务,一个员工不能同时做两个任务,求最少需要招募多少员工。

2.你手底下有1e9个员工,为了提高他们的工作效率,你会定期选取编号为l~r的员工发放奖金1元,求n轮之后被发放奖金最多的员工收到了多少次奖金

...

题目分析:

如果不看数据范围的话,最简单的思路就是把所有的区间都遍历一遍加上一,再遍历所有的区间,看看最大的数字是多少就行了。但是这样的复杂度是O(n*1e9),炸出了天际。如果稍微有点算法基础,比如小白我,马上会想到可以差分优化一步,要让区间[l,r]都加上1,可以a[l]++,a[r+1]--。但是我们不可能开出一个大小为1e9的数组,这时我想到了可以用map优化,这样算法复杂度就变成了O(n+1e9*log(1e9)),基本上一般的评测机还是会炸,但是碰巧这题所在的评测机给出了15s的上限

于是我交了一发玩玩

 第一版代码:

#include<bits/stdc++.h>
using namespace std;
map<int,int>mp;
signed main()
{
    int n;
    cin >> n;
    int ans = 0;
    for(int i = 1;i<=n;i++){
        int l,r;
        cin >> l >> r;//对了忘说了,这个题给的是左区间和区间长度,下面同理
        mp[l]++;
        mp[l+r]--;
    }
    int cnt = 0;
    for(int i = 1;i<=1e9;i++){
        if(mp.find(i)!=mp.end())cnt+=mp[i];
        ans = max(ans,cnt);
    }
    cout << ans << endl;   
}

嘿,所有测试点跑了快两分钟,就在我担心是不是把评测机玩炸了的时候,它给过了!

但是这屎一样的复杂度,过了也没什么骄傲的,主要图一乐,随后我又交了一发,TLE了

只能说意料之中,接着我开始考虑第二次优化。

优化思路:

按照差分的思想往后一延伸,每个区间其实也就提供了左端加,右端减两个变化

我没必要遍历一遍区间[1,1e9],我只需要知道某个变化的点需要跳到哪个点去就行了

总共2*n个点,把每个点所在的位置排个序再遍个历,这样时间复杂度就被优化到了O(nlog(n))

终版代码:

 

#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
const int N = 2e6+5;
struct node{
    int id;//位置
    int xx;//状态
}a[N];

signed main()
{
    IOS
    int n;
    cin >> n;
    int ans = 0;
    int left,right;
    for(int i = 1;i<=n;i++){
        int l,r;
        cin >> l >> r;
        a[i].id = l;
        a[i+n].id = l+r;
        a[i].xx = 1;
        a[i+n].xx = -1;
    }
    sort(a+1,a+n*2+1,[&](node x,node y){
        if(x.id!=y.id)return x.id<y.id;
        return x.xx<y.xx;
        //这里注意排序的时候-1应该要在+1的前面,因为这个位置前面的区间已经覆盖不到了
    });
    int cnt = 0;
    for(int i = 1;i<=2*n;i++){
        cnt += a[i].xx;
        // ans = max(ans,cnt);
        if(ans<cnt){
            ans = cnt;
            // left = a[i].id;
            // right = a[i+1].id-1;
        }
    }
    // cout << left << " " << right << endl;
    cout << ans << endl;   
}

同时会发现,如果把我注释的地方稍微修改一下,就可以得到具体是在哪个区间了

至此,所有优化完成。

感想:

虽然题目不难,就是基于差分思想进行排序的一个过程,但是给我的启发却很大。也许题目一眼看不出来思路的时候,我可以先从简单的思维入手,一步步进行优化,反而更容易得到答案。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值