贪心 - 区间问题(区间选点 + 最大不相交区间数量 + 区间分组 + 区间覆盖)

贪心 - 区间问题(区间选点 + 最大不相交区间数量 + 区间分组 + 区间覆盖)

1、区间选点

给定N个闭区间[ai,bi],请你在数轴上选择尽量少的点,使得每个区间内至少包含一个选出的点。

输出选择的点的最小数量。

位于区间端点上的点也算作区间内。

输入格式
第一行包含整数N,表示区间数。

接下来N行,每行包含两个整数ai,bi,表示一个区间的两个端点。

输出格式
输出一个整数,表示所需的点的最小数量。

数据范围
1≤N≤105,
−109≤ai≤bi≤109

输入样例:
3
-1 1
2 4
3 5
输出样例:
2

思路:

将 给 定 区 间 按 右 端 点 从 小 到 大 排 序 。 将给定区间按右端点从小到大排序。

每 次 判 断 当 前 区 间 的 右 端 点 r i 能 否 覆 盖 到 后 面 的 区 间 , 即 判 断 r i > = l i + k , k = 1 , 2 , . . . 。 每次判断当前区间的右端点r_i能否覆盖到后面的区间,即判断r_i>=l_{i+k},k=1,2,...。 riri>=li+kk=1,2,...

若 能 , 则 将 这 k 个 区 间 加 入 到 同 一 集 合 , 表 示 这 些 集 合 中 的 区 间 均 包 含 点 r i 。 若 不 能 , 则 将 r i 更 新 为 r i + 1 , 同 时 答 案 需 要 增 加 1 。 再 看 r i + 1 最 多 能 够 覆 盖 多 少 个 区 间 。 若能,则将这k个区间加入到同一集合,表示这些集合中的区间均包含点r_i。\\若不能,则将r_i更新为r_{i+1},同时答案需要增加1。再看r_{i+1}最多能够覆盖多少个区间。 kririri+11ri+1

目 标 就 是 选 尽 量 少 的 点 , 能 够 使 得 涵 盖 了 所 有 区 间 。 按 右 端 点 排 序 后 , 因 为 每 个 区 间 都 要 被 分 组 , 那 么 选 择 右 端 点 就 能 够 涵 盖 到 更 多 的 区 间 , 使 得 分 的 组 数 最 小 。 目标就是选尽量少的点,能够使得涵盖了所有区间。\\按右端点排序后,因为每个区间都要被分组,那么选择右端点就能够涵盖到更多的区间,使得分的组数最小。 使使

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>

#define P pair<int,int>
#define r first
#define l second

using namespace std;

const int N=1e5+10;

int n;
P seg[N];

int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++) scanf("%d%d",&seg[i].l,&seg[i].r);
    
    sort(seg,seg+n);
    
    int res=1,t=seg[0].r;
    for(int i=1;i<n;i++)
        if(t<seg[i].l)
        {
            t=seg[i].r;
            res++;
        }
    
    cout<<res<<endl;
    
    return 0;
}

2、最大不相交区间数量

给定N个闭区间[ai,bi],请你在数轴上选择若干区间,使得选中的区间之间互不相交(包括端点)。

输出可选取区间的最大数量。

输入格式
第一行包含整数N,表示区间数。

接下来N行,每行包含两个整数ai,bi,表示一个区间的两个端点。

输出格式
输出一个整数,表示可选取区间的最大数量。

数据范围
1≤N≤105,
−109≤ai≤bi≤109

输入样例:
3
-1 1
2 4
3 5
输出样例:
2

思路:

将 给 定 区 间 按 右 端 点 从 小 到 大 排 序 。 将给定区间按右端点从小到大排序。

每 次 判 断 当 前 区 间 的 右 端 点 r i 与 后 面 的 区 间 是 否 有 交 集 , 即 判 断 r i > = l i + k , k = 1 , 2 , . . . 。 每次判断当前区间的右端点r_i与后面的区间是否有交集,即判断r_i>=l_{i+k},k=1,2,...。 riri>=li+kk=1,2,...

若 能 , 说 明 两 个 区 间 有 交 集 , 则 答 案 需 要 增 加 1 , 同 时 再 看 r i + 1 , 做 同 样 判 断 。 若能,说明两个区间有交集,则答案需要增加1,同时再看r_{i+1},做同样判断。 1ri+1
若 不 能 , 说 明 两 个 区 间 无 交 集 , 则 将 这 k 个 区 间 加 入 到 集 合 中 , 表 示 集 合 中 的 区 间 没 有 交 集 。 若不能,说明两个区间无交集,则将这k个区间加入到集合中,表示集合中的区间没有交集。 k

代 码 同 第 一 题 完 全 相 同 。 代码同第一题完全相同。

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>

#define P pair<int,int>
#define r first
#define l second

using namespace std;

const int N=1e5+10;

int n;
P seg[N];

int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++) scanf("%d%d",&seg[i].l,&seg[i].r);
    
    sort(seg,seg+n);
    
    int res=1,t=seg[0].r;
    for(int i=1;i<n;i++)
        if(t<seg[i].l)
        {
            t=seg[i].r;
            res++;
        }
        
    cout<<res<<endl;
    
    return 0;
}

3、区间分组

给定N个闭区间[ai,bi],请你将这些区间分成若干组,使得每组内部的区间两两之间(包括端点)没有交集,并使得组数尽可能小。

输出最小组数。

输入格式
第一行包含整数N,表示区间数。

接下来N行,每行包含两个整数ai,bi,表示一个区间的两个端点。

输出格式
输出一个整数,表示最小组数。

数据范围
1≤N≤105,
−109≤ai≤bi≤109

输入样例:
3
-1 1
2 4
3 5
输出样例:
2

思路:

先 将 给 定 区 间 按 左 端 点 从 小 到 大 排 序 。 先将给定区间按左端点从小到大排序。

对 于 每 个 给 定 区 间 , 每 次 判 断 当 前 区 间 能 否 加 入 到 已 经 分 好 的 组 中 去 。 即 判 断 已 经 分 好 的 组 中 , 每 个 组 所 有 区 间 中 的 最 右 边 的 端 点 m a x _ r 与 当 前 区 间 的 左 端 点 l i 的 大 小 关 系 。 对于每个给定区间,每次判断当前区间能否加入到已经分好的组中去。\\即判断已经分好的组中,每个组所有区间中的最右边的端点max\_r与当前区间的左端点l_i的大小关系。 max_rli

若 m a x _ r < l i , 说 明 当 前 区 间 不 能 加 入 到 该 组 , 反 之 说 明 可 以 加 入 到 该 组 。 若max\_r<l_i,说明当前区间不能加入到该组,反之说明可以加入到该组。 max_r<li
若 已 经 分 好 的 组 中 , 没 有 任 何 一 组 的 m a x _ r < l i , 则 需 重 新 开 一 个 组 将 当 前 区 间 加 入 进 去 。 若已经分好的组中,没有任何一组的max\_r<l_i,则需重新开一个组将当前区间加入进去。 max_r<li

可 见 整 个 过 程 需 要 频 繁 地 查 询 和 修 改 某 一 组 的 m a x _ r , 这 个 过 程 可 以 通 过 小 根 堆 来 维 护 。 可见整个过程需要频繁地查询和修改某一组的max\_r,这个过程可以通过小根堆来维护。 max_r
用 一 个 小 根 堆 来 存 储 各 组 的 最 右 端 点 。 用一个小根堆来存储各组的最右端点。

① 、 若 当 前 区 间 的 左 端 点 l i 小 于 等 于 堆 顶 元 素 , 说 明 需 要 重 新 开 一 个 组 将 当 前 区 间 加 入 进 去 , 同 时 需 要 加 入 该 区 间 的 右 端 点 到 堆 。 ①、若当前区间的左端点l_i小于等于堆顶元素,说明需要重新开一个组将当前区间加入进去,\\\qquad同时需要加入该区间的右端点到堆。 li

② 、 若 当 前 区 间 的 左 端 点 大 于 堆 顶 元 素 , 说 明 可 以 将 该 区 间 加 入 到 这 个 组 , 同 时 这 个 组 的 最 右 端 点 需 要 更 新 为 当 前 区 间 的 右 端 点 。 ②、若当前区间的左端点大于堆顶元素,说明可以将该区间加入到这个组,\\\qquad同时这个组的最右端点需要更新为当前区间的右端点。

最 终 , 堆 中 元 素 的 个 数 就 是 分 组 的 个 数 。 最终,堆中元素的个数就是分组的个数。

代码:

#include<cstdio>
#include<algorithm>
#include<queue>

#define P pair<int,int>
#define l first
#define r second

using namespace std;

const int N=1e5+10;

int n;
P seg[N];

int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++) scanf("%d%d",&seg[i].l,&seg[i].r);
    
    sort(seg,seg+n);
    
    priority_queue<int,vector<int>,greater<int>> heap;
    heap.push(seg[0].r);
    for(int i=1;i<n;i++)
    {
        int t=heap.top();
        if(seg[i].l<=t) heap.push(seg[i].r);
        else
        {
            heap.pop();
            heap.push(seg[i].r);
        }
    }
    
    printf("%d\n",heap.size());
    
    return 0;
    
}

4、区间覆盖

给定N个闭区间[ai,bi]以及一个线段区间[s,t],请你选择尽量少的区间,将指定线段区间完全覆盖。

输出最少区间数,如果无法完全覆盖则输出-1。

输入格式
第一行包含两个整数s和t,表示给定线段区间的两个端点。

第二行包含整数N,表示给定区间数。

接下来N行,每行包含两个整数ai,bi,表示一个区间的两个端点。

输出格式
输出一个整数,表示所需最少区间数。

如果无解,则输出-1。

数据范围
1≤N≤105,
−109≤ai≤bi≤109,
−109≤s≤t≤109

输入样例:
1 5
3
-1 3
2 4
3 5
输出样例:
2

思路:

首 先 将 给 定 区 间 按 左 端 点 从 小 到 大 排 序 。 首先将给定区间按左端点从小到大排序。

对 于 所 有 能 够 覆 盖 起 点 s t 的 区 间 , 我 们 选 择 其 中 右 端 点 l j 最 大 的 一 个 区 间 , 接 着 将 s t 更 新 为 l j 。 对于所有能够覆盖起点st的区间,我们选择其中右端点l_j最大的一个区间,接着将st更新为l_j。 stljstlj

初 始 化 起 点 s t = s , i 从 前 到 后 遍 历 每 个 区 间 , 用 双 指 针 j 来 寻 找 能 够 覆 盖 s t 的 区 间 的 右 端 点 的 最 大 值 r 。 初始化起点st=s,i从前到后遍历每个区间,用双指针j来寻找能够覆盖st的区间的右端点的最大值r。 st=sijstr

① 、 若 没 有 任 何 区 间 能 够 覆 盖 s t , 可 提 前 退 出 循 环 , 输 出 − 1 。 ①、若没有任何区间能够覆盖st,可提前退出循环,输出-1。 st退1

② 、 若 已 经 覆 盖 了 终 点 e , 可 提 前 退 出 , 输 出 所 需 的 区 间 数 量 r e s 。 ②、若已经覆盖了终点e,可提前退出,输出所需的区间数量res。 e退res

③ 、 否 则 就 更 新 s t 为 r , i 移 到 r 所 在 区 间 的 后 一 个 区 间 , 重 复 以 上 操 作 。 ③、否则就更新st为r,i移到r所在区间的后一个区间,重复以上操作。 strir

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>

#define P pair<int,int>
#define l first
#define r second

using namespace std;

const int N=1e5+10;

int n,s,e;
P seg[N];

int main()
{
    scanf("%d%d%d",&s,&e,&n);
    for(int i=0;i<n;i++) scanf("%d%d",&seg[i].l,&seg[i].r);
    
    sort(seg,seg+n);
    
    int st=s,res=0;
    bool flag=false;
    for(int i=0;i<n;i++)
    {
        int j=i,r=-2e9;
        while(j<n && seg[j].l<=st)
        {
            r=max(r,seg[j].r);
            j++;
        }
        
        if(r<st)
        {
            res=-1;
            break;
        }
        
        res++;
        if(r>=e)
        {
            flag=true;
            break;
        }
        
        st=r;
        i=j-1;
    }
    
    if(!flag) res=-1;
    cout<<res<<endl;
    
    return 0;
}
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值