区间覆盖问题(总结)

转载自:https://blog.csdn.net/xianglunxi/article/details/9448951 

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

样例:

区间长度8,可选的覆盖线段[2,6],[1,4],[3,6],[3,7],[6,8],[2,4],[3,5]

解题过程:

1将每一个区间按照左端点递增顺序排列,拍完序后为[1,4],[2,4],[2,6],[3,5],[3,6],[3,7],[6,8]

2设置一个变量表示已经覆盖到的区域。再剩下的线段中找出所有左端点小于等于当前已经覆盖到的区域的右端点的线段中,右端点最大的线段在加入,直到已经覆盖全部的区域

3过程:

假设第一步加入[1,4],那么下一步能够选择的有[2,6],[3,5],[3,6],[3,7],由于7最大,所以下一步选择[3,7],最后一步只能选择[6,8],这个时候刚好达到了8退出,所选区间为3

4贪心证明:

需要最少的线段进行覆盖,那么选取的线段必然要尽量长,而已经覆盖到的区域之前的地方已经无所谓了,(可以理解成所有的可以覆盖的左端点都是已经覆盖到的地方),那么真正能够使得线段更成的是右端点,左端点没有太大的意义,所以选择右端点来覆盖

(2)最大不相交覆盖

问题描述:给定一个长度为m的区间,再给出n条线段的起点和终点(开区间和闭区间处理的方法是不同,这里以开区间为例),问题是从中选取尽量多的线段,使得每个线段都是独立的,就是不和其它有任何线段有相交的地方


样例:

区间长度8,可选的覆盖线段[2,6],[1,4],[3,6],[3,7],[6,8],[2,4],[3,5]

解题过程:

对线段的右端点进行升序排序,每加入一个线段,然后选择后面若干个(也有可能是一个)右端点相同的线段,选择左端点最大的那一条,如果加入以后不会跟之前的线段产生公共部分,那么就加入,否则就继续判断后面的线段

1排序:将每一个区间按右端点进行递增顺序排列,拍完序后为[1,4],[2,4],[2,6],[3,5],[3,6],[3,7],[6,8]

2第一步选取[2,4],发现后面只能加入[6,8],所以区间的个数为2


贪心证明:因为需要尽量多的独立的线段,所以每个线段都尽可能的小,对于同一右端点,左端点越大,线段长度越小。那么为什么要对右端点进行排序呢?如果左端点进行排序,那么右端点是多少并不知道,那么每一条线段都不能对之前所有的线段进行一个总结,那么这就明显不满足贪心的最有字结构了。

(3)区间选点问题

问题描述:给定一个长度为m的区间,再给出n条线段和这n条线段需要满足的要求(要求是这n条线段上至少有的被选择的点的个数),问题是整个区间内最少选择几个点,使其满足每一条线段的要求.

样例:略

解题过程:将每个线段按照终点坐标进行递增排序,相同终点的前点坐标大的在前面,一个个将其满足

贪心证明:要想使得剩下的线段上选择的点最少,那么就应该尽量使得已经选择了的点尽量能在后面的线段中发挥作用,而我们是从左往右选择线段的,那么要使得选取的点能满足后面线段的要求,那么必须是从线段的有端点开始选点,那么问题(2)一样涉及到一个问题,如果是按照线段的左端点对线段进行排序的话,不知道右端点的话,每一条线段都不能对之前已经操作过的所有线段进行一个总结,那么这就同样不满足贪心算法的最优子结构性质了。

可以解决的实际问题:数轴上面有n个闭区间[a,b],取尽量少的点,使得每个区间内都至少有一个点(不同区间内含的点可以是同一个)

实例(类似第一种区间覆盖,只不过求最大值)

三个农民每天清晨5点起床,然后去牛棚给3头牛挤奶。第一个农民在300时刻(从5点开始计时,秒为单位)给他的牛挤奶,一直到1000时刻。第二个农民在700时刻开始,在 1200时刻结束。第三个农民在1500时刻开始2100时刻结束。期间最长的至少有一个农民在挤奶的连续时间为900秒(从300时刻到1200时刻),而最长的无人挤奶的连续时间(从挤奶开始一直到挤奶结束)为300时刻(从1200时刻到1500时刻)。  你的任务是编一个程序,读入一个有N个农民(1 <= N <= 5000)挤N头牛的工作时间列表,计算以下两点(均以秒为单位):  最长至少有一人在挤奶的时间段。  最长的无人挤奶的时间段。(从有人挤奶开始算起)
Input
Line 1:  一个整数N。  Lines 2..N+1:  每行两个小于1000000的非负整数,表示一个农民的开始时刻与结束时刻。 
Output
一行,两个整数,即题目所要求的两个答案。
Sample Input
 3
300 1000
700 1200
1500 2100
Sample Output
 900 300
参考代码:

//算法思想:区间覆盖,先排序,总共就两种情况。
#include<iostream>
#include<stdio.h>
#include<fstream>
#include<algorithm>
using namespace std;
#define Max(a,b) a>b?a:b
typedef struct
{
  int s_time;
  int e_time;
}D_farmers;
D_farmers df[5001];
int n;
bool cmp(D_farmers a,D_farmers b)//起始时间的升序排序
{
    return a.s_time<b.s_time||(a.s_time==b.s_time&&a.e_time<b.e_time);
}
int main()
{
    ifstream fin("milk2.in");
    ofstream fout("milk2.out");
    int i,max1_t=0,max2_t=0,s_max,t_max;//s_max和t_max分别记录最大起始时间和结束时间
    fin>>n;
    for(i=0;i<n;i++)
        fin>>df[i].s_time>>df[i].e_time;
 
    sort(df,df+n,cmp);//升序排序
    s_max=df[0].s_time;
    t_max=df[0].e_time;
    max1_t=df[0].e_time-df[0].s_time;
    //max2_t=df[0].s_time;
 
    for(i=1;i<n;i++)
    {
     if(t_max>=df[i].s_time&&df[i].e_time>=t_max)//前一个结束时间大于当前起始时间,更新新的截至时间,
        t_max=df[i].e_time;
     else if(t_max<df[i].s_time)//前一个结束时间小于当前起始时间,进行判断比较
      {
         //cout<<t_max<<endl;
         max1_t=Max(t_max-s_max,max1_t);
         //cout<<max1_t<<endl;
         max2_t=Max(df[i].s_time-t_max,max2_t);
         s_max=df[i].s_time;
         t_max=df[i].e_time;
      }
 
    }
    //cout<<max1_t<<" "<<max2_t<<endl;
    fout<<max1_t<<" "<<max2_t<<endl;
    return 0;
}

 

  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值