三类贪心区间覆盖问题

一、区间完全覆盖问题

题目

给定一个长度为m的区间,再给出n条线段的起点和终点(注意这里是闭区间),求最少使用多少条线段可以将整个区间完全覆盖。

解析

先将所有线段按起点从小到大排序。排完序后,枚举每一个线段(被其它线段包含的线段不用考虑,因为很明显包含它的线段比它更优),将其作为最左端的线段,

再在剩下的左端点小于等于最左端的线段的右端点的线段中(若没有则无解),找到右端点最大的一个线段,即贪心,很显然这是最优的,因为其左端都被最左端的线段覆盖了,

也就没有覆盖到任何地方,则其右端点越大,其右端覆盖到的地方也就最优。

反复重复上一步,直到覆盖完整个长度为m的区间,就能得到最少的线段数。

Code

#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
using namespace std;
struct rec{
    int l,r;
}a[1001];
bool cmp(rec x,rec y)
{
    return x.l<y.l;        //按左端点从小到大排
}
int m,n,ll,minn=0x7f7f7f7f;
void q(int x,int ans)
{
    if(a[x].r-ll>=m-1)    //覆盖总长度达到
    {
        minn=min(minn,ans);
        return ;
    }
    int temp=0;
    for(int i=x+1;i<=n;i++)
    {
        if(a[i].l<=a[x].r)    //找左端点小于当前线段右端点的
        {
            if(a[i].r>a[temp].r) temp=i;    //找右端点最大区间
        }
        else break;        //顺序排序,如果左端点大于当前线段右端点,后面肯定也大于
        if(temp!=0) q(temp,ans+1);
    }
}
int main()
{
    a[0].r=-1;    //特殊处理
    cin>>m>>n;
    for(int i=1;i<=n;i++) cin>>a[i].l>>a[i].r;
    sort(a+1,a+n+1,cmp);    //顺序排序
    for(int i=1;i<=n;i++)
    {
        ll=a[i].l;        //记录起点
        q(i,1);
    }
    cout<<minn;
    return 0;
}

二、最大不相交区间数问题

题目

数轴上有n个开区间[ai,bi],要求选择尽量多个区间,使得这些区间两两没有公共点。

解析

先对区间左端点进行从大到小排序,左端点相同按右端点从小到大排序,
再依次选出左端点最大的区间,当待选择区间与已选区间集合相交时,舍弃待选区间,
每次直接从排好后的第一个开始选,是为了使左边预留区间最大。

Code

#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
using namespace std;
struct rec{
    int a,b;
}ra[1001];
bool cmp(rec x,rec y)
{
    if(x.a!=y.a) return x.a>x.b;//左端点从大到小排序
    else return y.a<y.b; //左端点相同,按右端点从小到大排序,即左端点相同,优先选择短的
}
int n,ans=1,lasta;
int main()
{
    cin>>n;
    for(int i=0;i<n;i++) cin>>ra[i].a>>ra[i].b;
    sort(ra,ra+n,cmp);
    lasta=ra[0].a;//选中a最大的第一个
    for(int i=1;i<n;i++)
    {
        if(ra[i].a<=lasta)    //不重叠
        {
            lasta=ra[i].a;
            ans++;
        }
    }
    cout<<ans;
    return 0;
}

三、区间选点问题

题目

数轴上有n个闭区间 [ai,bi],要求选取尽量少的点,使得每个区间内都至少有一个点(不同区间内含的点可以是同一个)。

解析

先按左端点从小到大排序每个区间,若左端点相同,则按右端点从小到大排序。

1.再从第一个区间贪心往后找,如果下一个区间左端点大于该区间的右端点,则需增加一个点,反之共用一个点;

2.若下个区间右端点小于当前区间右端点,说明共用的区间范围变小了,则更新区间右端点为下一个区间右端点。

不断重复1、2两个步骤,直到每个区间都有点。

为什么呢?因为在排完序后,

①当b1>bi时,显然此时一个点能覆盖最大的区域右边界变为bi;

②当b1<ai时,显然一个点不能覆盖到区间i上,所以需新开一个点,此时能覆盖的区域最右边界变为bi;

③ 当b1<bi时,显然区间1和区间i有公共的部分,但此时一个点能覆盖的区域最右边界还是为b1,无需更新区域最右边界。

综上所述,排序后贪心选点是正确的。

Code

#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
using namespace std;
struct rec{
    int a,b;
}ra[1001];
bool cmp(rec x,rec y)
{
    if(x.a!=y.a) return x.a<y.a;    //按左端点从小到大排序
    else return x.b<y.b;        //左端点相同,则按右端点从小到大排序
}
int n,ans=1,r;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>ra[i].a>>ra[i].b;
    sort(ra+1,ra+n+1,cmp);        //顺序排序
    r=ra[1].b;    //记录上一个区间右端点
    for(int i=2;i<=n;i++)
    {
        if(ra[i].a>r)    //左端点大于上个区间右端点
        {
            ans++;        //增加一个点
            r=ra[i].b;
        }
        else if(ra[i].b<r) r=ra[i].b;    //右端点小于上个区间右端点,更新上个区间右端点
    }
    cout<<ans;
    return 0;
}

原文地址:https://www.cnblogs.com/I-Love-You-520/p/11147003.html

时间: 2024-10-08 10:03:38

三类贪心区间覆盖问题的相关文章

10382 - Watering Grass(贪心 区间覆盖问题)洒水面覆盖

double qiuzhi(int id) { double t1=cc[id].rid*cc[id].rid; double t2=w*w/4; double t3=t1-t2; double t4=sqrt(t3); return t4; } void to_qujian() { for(int i=0; i<t; i++) { double zhi=qiuzhi(i); qq[i].a=cc[i].pos-zhi; qq[i].b=cc[i].pos+zhi; } } 典型的贪心区间覆盖问

poj 2376 贪心 区间覆盖问题

题意: 给n个区间 选择尽量少的区间 覆盖1~T这个区间 如果不能覆盖 输出-1 思路: 经典贪心 区间覆盖 将所有区间按照起点从小到大排序 取终点在最右边的那个区间 code: #include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #include<vector> #include<string> #include<queue>

FZU 2144(几何+贪心区间覆盖)

题意:三维空间给出n个蚊子的初始位置(ax,ay,az)和移动趋势(dx,dy,dz),那么每个蚊子坐标随时间变化的函数就是(ax+dx*t, ay+dy*t, ax+dz*t).每次射杀一枪,可以把距离原点距离len之内的蚊子全部杀死.问最多能射杀几只蚊子,这时至少要射杀几次? 解法:先求出每只蚊子在射程之内的时间区间,即(ax+dx*t, ay+dy*t, ax+dz*t)^2<=len:解一元二次方程.如果两个解都小于0,则说明此蚊子不会被杀死.算出所有能被射杀的蚊子的时间区间以后就变成了

贪心--区间覆盖及区间选点问题

区间覆盖: 数轴上有若干区间,选用最少的线段覆盖指定区间. 区间选点:选用最少的区间使每个区间内至少有一个点 样题1: J - Muddy roads Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Description Farmer John has a problem: the dirt road from his farm to town has suffered in the re

nyoj 12——喷水装置二——————【贪心-区间覆盖】

喷水装置(二) 时间限制:3000 ms  |  内存限制:65535 KB 难度:4 描述 有一块草坪,横向长w,纵向长为h,在它的橫向中心线上不同位置处装有n(n<=10000)个点状的喷水装置,每个喷水装置i喷水的效果是让以它为中心半径为Ri的圆都被润湿.请在给出的喷水装置中选择尽量少的喷水装置,把整个草坪全部润湿. 输入 第一行输入一个正整数N表示共有n次测试数据.每一组测试数据的第一行有三个整数n,w,h,n表示共有n个喷水装置,w表示草坪的横向长度,h表示草坪的纵向长度.随后的n行,

南阳OJ-12-喷水装置(二)贪心+区间覆盖

题目链接: http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=12 题目大意: 有一块草坪,横向长w,纵向长为h,在它的橫向中心线上不同位置处装有n(n<=10000)个点状的喷水装置,每个喷水装置i喷水的效果是让以它为中心半径为Ri的圆都被润湿.请在给出的喷水装置中选择尽量少的喷水装置,把整个草坪全部润湿. 传送门:喷水装置(一) 思路: 区间覆盖问题,每个点有一段喷水区间,按照左端点排序即可,设置两变量begin, end,依次扫过去,每

NYOJ 题目710 外星人的供给站(贪心区间覆盖)

外星人的供给站 时间限制:1000 ms  |  内存限制:65535 KB 难度:3 描述 外星人指的是地球以外的智慧生命.外星人长的是不是与地球上的人一样并不重要,但起码应该符合我们目前对生命基本形式的认识.比如,我们所知的任何生命都离不开液态水,并且都是基于化学元素碳(C)的有机分子组合成的复杂有机体. 42岁的天文学家Dr. Kong已经执著地观测ZDM-777星球十多年了,这个被称为"战神"的红色星球让他如此着迷.在过去的十多年中,他经常有一些令人激动的发现.ZDM-777星

nyoj 12 喷水装置(二) 贪心 区间覆盖

思路很简单 根据坐标和半径 得到每个喷水设置能够覆盖的左右坐标 然后按左坐标从小到大排序 筛选时候如果喷水设置的左坐标小于等于起始点star用while循环寻找能够覆盖最大的右坐标 然后更新起始点star为寻找到的最大右坐标 具体看代码: #include <stdio.h> #include <math.h> #include <algorithm> using namespace std; struct node { double left,right; }c[10

hdoj 1050 Moving Tables【贪心区间覆盖】

Moving Tables Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 24316    Accepted Submission(s): 8053 Problem Description The famous ACM (Advanced Computer Maker) Company has rented a floor of a b