区间覆盖--最少能覆盖区间的区间数(贪心)

问题描述

大表哥分配 N (1 <= N <= 25,000) 只中的一些奶牛在牛棚附近做些清洁。 他总是要让至少一只牛做清洁。他把一天分成T段(1 <= T <= 1,000,000), 第一段是1,最后一段是T

每只奶牛只在一些时间段有空。奶牛如果选择某一段时间,则必须完成整段时间的工作

你的任务是帮助FJ安排一些奶牛,使每段时间至少有一只奶牛被安排来做这件事。并且奶牛数应尽可能小。如果不可能办到,输出-1

Input

注意,输入包含多组测试数据,请处理到文件结束
第一行:N和T
第二行至N+1行: 每一行一个闭区间,表示奶牛工作的开始时间和结束时间。

Output

选择的区间的数目,不可能办到输出-1

Sample Input

3 10
1 7
3 6
6 10

Sample Output

2

提示

这道题输入数据很多,请用scanf而不是cin

解题思路

问题分析

首先分析这是一个什么问题,题目要求选择最少的区间来覆盖 [ 1 , t ] [1,t] [1,t]这个区间,这个题可以使用区间dp+线段树来做(大材小用,区间dp+线段树可以解决每个区间花费不同的区间最小花费问题,用在这里自然可以),但是更好的算法应该还是贪心。

注意坑点

[ 1 , 3 ] [1,3] [1,3] [ 4 , 10 ] [4,10] [4,10]是可以覆盖 [ 1 , 10 ] [1,10] [1,10]的。

贪心策略

我们先考虑按照区间的左端点从小到大排序,为什么这么排呢?因为我们要让奶牛覆盖完整的区间,那么肯定先选能够在1节点工作的奶牛,假设这个奶牛工作的区间是 [ 1 , b 1 ] [1,b_1] [1,b1],那么下一个要选的奶牛,就是从 b 1 b_1 b1开始,工作时间最久的(假设其工作区间是 [ a 2 , b 2 ] [a_2,b_2] [a2,b2]),也就是需要 a 2 < = b 1 + 1 a_2<=b_1+1 a2<=b1+1(+1是因为我们上面提到的坑点)。由于我们开始是按照区间的左端点从小到大排序,那么从循环开始,后面连续一段应该都是 a i < b 1 a_i<b_1 ai<b1的(如果没有,设个标记直接跳出,因为没有办法完全覆盖了),然后用while处理找工作时间最长的即可。更多详细信息见下方代码。

完整代码

//#pragma GCC optimize(2)//比赛禁止使用!
//#pragma G++ optimize(2)
//#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <climits>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;

const int maxn=25000+1;
struct Interval
{
    int a,b;
}len[maxn];
int n,t,ans;
bool comp(Interval a,Interval b)//a小的排在前面
{
    return a.a<b.a;
}
int getint()
{
    int x=0,s=1;
    char ch=' ';
    while(ch<'0' || ch>'9')
    {
        ch=getchar();
        if(ch=='-') s=-1;
    }
    while(ch>='0' && ch<='9')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*s;
}
int main()
{
//    ios::sync_with_stdio(false);
//    cin.tie(0);

    n=getint(); t=getint();
    for (int i=1; i<=n; i++)
        len[i].a=getint(),len[i].b=getint();

    sort(len+1,len+1+n,comp);

    int temp=0;//初始值赋为0,可以在后面的循环中观察有无奶牛早早去工作(不赋为1是为了在后面的while中加了1
    int tempmax=INT_MIN;
    int id=0;
    bool flag=false;//用于判断是否全部覆盖
    for (int i=1; i<=n; i++)
    {
        flag=false;
        while(len[i].a<=temp+1 && i<=n)//这段区间可以作为备选利用就进入循环,此处加一很关键,因为(1,2),(3,4)算覆盖了(1,4)
        {
            flag=true;//flag=true保证所有区间都可以覆盖上
            if(len[i].b-temp>tempmax)//选从temp开始工作时间最长的
            {
                tempmax=len[i].b-temp;
                id=i;//记录这个从temp开始工作时间最长的奶牛
            }
            i++;
        }
        if(!flag) break;//找不到可以利用的区间,覆盖失败,直接跳出
        ans++;
        i--;//如果进入了循环,最后多加了一次
        temp=len[id].b;//选中的节点取从上一个节点开始工作时间最长的
        if(temp>=t) break;//已经找到足够的奶牛,直接跳出
        tempmax=INT_MIN;

    }
    if(!flag || temp<t) printf("-1\n");
    else printf("%d\n",ans);
    return 0;
}

部分易错数据

Input1
4 100
21 50
50 81
1 20
80 99
Output1
-1
Input2
3 10
1 3
4 6
7 10
Output2
3
Input3
3 10
2 4
3 7
6 10
Output3
-1
  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值