区间覆盖问题

171 篇文章 0 订阅
95 篇文章 0 订阅



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

参考代码:

  1. //算法思想:区间覆盖,先排序,总共就两种情况。  
  2. #include<iostream>  
  3. #include<stdio.h>  
  4. #include<fstream>  
  5. #include<algorithm>  
  6. using namespace std;  
  7. #define Max(a,b) a>b?a:b  
  8. typedef struct  
  9. {  
  10.   int s_time;  
  11.   int e_time;  
  12. }D_farmers;  
  13. D_farmers df[5001];  
  14. int n;  
  15. bool cmp(D_farmers a,D_farmers b)//起始时间的升序排序  
  16. {  
  17.     return a.s_time<b.s_time||(a.s_time==b.s_time&&a.e_time<b.e_time);  
  18. }  
  19. int main()  
  20. {  
  21.     ifstream fin("milk2.in");  
  22.     ofstream fout("milk2.out");  
  23.     int i,max1_t=0,max2_t=0,s_max,t_max;//s_max和t_max分别记录最大起始时间和结束时间  
  24.     fin>>n;  
  25.     for(i=0;i<n;i++)  
  26.         fin>>df[i].s_time>>df[i].e_time;  
  27.   
  28.     sort(df,df+n,cmp);//升序排序  
  29.     s_max=df[0].s_time;  
  30.     t_max=df[0].e_time;  
  31.     max1_t=df[0].e_time-df[0].s_time;  
  32.     //max2_t=df[0].s_time;  
  33.   
  34.     for(i=1;i<n;i++)  
  35.     {  
  36.      if(t_max>=df[i].s_time&&df[i].e_time>=t_max)//前一个结束时间大于当前起始时间,更新新的截至时间,  
  37.         t_max=df[i].e_time;  
  38.      else if(t_max<df[i].s_time)//前一个结束时间小于当前起始时间,进行判断比较  
  39.       {  
  40.          //cout<<t_max<<endl;  
  41.          max1_t=Max(t_max-s_max,max1_t);  
  42.          //cout<<max1_t<<endl;  
  43.          max2_t=Max(df[i].s_time-t_max,max2_t);  
  44.          s_max=df[i].s_time;  
  45.          t_max=df[i].e_time;  
  46.       }  
  47.   
  48.     }  
  49.     //cout<<max1_t<<" "<<max2_t<<endl;  
  50.     fout<<max1_t<<" "<<max2_t<<endl;  
  51.     return 0;  
  52. }  

以下内容复制于http://blog.csdn.net/dgq8211/article/details/7534776


先来看看什么是区间选点问题



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

贪心策略:

按照b1<=b2<=b3…(b相同时按a从大到小)的方式排序排序,从前向后遍历,当遇到没有加入集合的区间时,选取这个区间的右端点b。

证明:

为了方便起见,如果区间i内已经有一个点被取到,我们称区间i被满足。

1、首先考虑区间包含的情况,当小区间被满足时大区间一定被满足。所以我们应当优先选取小区间中的点,从而使大区间不用考虑。

      按照上面的方式排序后,如果出现区间包含的情况,小区间一定在大区间前面。所以此情况下我们会优先选择小区间。

      则此情况下,贪心策略是正确的。

2、排除情况1后,一定有a1<=a2<=a3……。


      对于区间1来说,显然选择它的右端点是明智的。因为它比前面的点能覆盖更大的范围。

      从而此情况下,贪心策略也是正确的。


[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include <algorithm>  
  3. using namespace std;  
  4. struct Extent  
  5. {  
  6.     int a,b;  
  7.     bool operator < (const Extent& S)const  
  8.     {  
  9.         return b < S.b || b == S.b && a > S.a;  
  10.     }  
  11. }A[10002];  
  12. int main()  
  13. {  
  14.     int z,n,cnt,end;  
  15.     scanf("%d",&z);  
  16.     while(z--)  
  17.     {  
  18.         cnt = 0;  
  19.         end = -1;  
  20.         scanf("%d",&n);  
  21.         for(int i=0;i<n;i++)  
  22.             scanf("%d%d",&A[i].a,&A[i].b);  
  23.         sort(A,A+n);  
  24.         for(int i=0;i<n;i++)  
  25.         {  
  26.             if(end < A[i].a)  
  27.             {  
  28.                 end = A[i].b;  
  29.                 cnt++;  
  30.             }  
  31.         }  
  32.         printf("%d\n",cnt);  
  33.     }  
  34.     return 0;  
  35. }  


例题:http://acm.nyist.net/JudgeOnline/problem.php?pid=287

Radar Installation
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 38208 Accepted: 8483

Description

Assume the coasting is an infinite straight line. Land is in one side of coasting, sea in the other. Each small island is a point locating in the sea side. And any radar installation, locating on the coasting, can only cover d distance, so an island in the sea can be covered by a radius installation, if the distance between them is at most d.

We use Cartesian coordinate system, defining the coasting is the x-axis. The sea side is above x-axis, and the land side below. Given the position of each island in the sea, and given the distance of the coverage of the radar installation, your task is to write a program to find the minimal number of radar installations to cover all the islands. Note that the position of an island is represented by its x-y coordinates.

Figure A Sample Input of Radar Installations


Input

The input consists of several test cases. The first line of each case contains two integers n (1<=n<=1000) and d, where n is the number of islands in the sea and d is the distance of coverage of the radar installation. This is followed by n lines each containing two integers representing the coordinate of the position of each island. Then a blank line follows to separate the cases.

The input is terminated by a line containing pair of zeros

Output

For each test case output one line consisting of the test case number followed by the minimal number of radar installations needed. "-1" installation means no solution for that case.

Sample Input

3 2
1 2
-3 1
2 1

1 2
0 2

0 0

Sample Output

Case 1: 2
Case 2: 1

Source

题目的意思就是给你一个坐标轴,雷达在x轴上,岛屿分布在x轴上方,给你岛屿的坐标以及雷达的最大扫描面积,求最少用几个雷达可以将所有的岛屿覆盖!


思路:

以岛为圆心,以d为半径画圆(d是雷达的辐射半径),其与x轴相交的区间为一个区  这样就变成了在区间内找最少的点问题了


[cpp]  view plain copy
  1. #include<iostream>  
  2. #include<cmath>  
  3. using namespace std;  
  4. typedef struct  
  5. {  
  6.     double l,r;  
  7. }in;  
  8. int cmp(const void *a, const void *b)  
  9. {  
  10.     return (*(in *)a).l >= (*(in *)b).l ? 1:-1;  
  11. }  
  12. int main()  
  13. {  
  14.     int n,d,i,x,y,sw,re,count = 1;  
  15.     double pre;  
  16.     in p[1000];  
  17.     while(1)  
  18.     {  
  19.         cin>>n>>d;  
  20.         if(n == 0 && d==0) break;  
  21.         sw = 1;  
  22.         for(i=0;i<n;i++)  
  23.         {  
  24.             cin>>x>>y;  
  25.             if(d>=y&&sw==1)  
  26.             {  
  27.                 p[i].l = x-sqrt((double)d*d - (double)y*y);  
  28.                 p[i].r = x+sqrt((double)d*d - (double)y*y);  
  29.             }  
  30.             else  
  31.             {  
  32.                 sw = 0;  
  33.             }  
  34.         }  
  35.         if(sw == 0)  
  36.         {  
  37.             cout<<"Case "<<count++<<": "<<-1<<endl;  
  38.             continue;  
  39.         }  
  40.         qsort(p,n,sizeof(in),cmp);   
  41.         re = 1;  
  42.         pre = p[0].r;  
  43.         for(i=1;i<n;i++)  
  44.         {  
  45.             if(p[i].l>pre)  
  46.             {  
  47.                 re++;  
  48.                 pre = p[i].r;  
  49.             }  
  50.             else  
  51.             {  
  52.                 if(p[i].r<pre)  
  53.                 {  
  54.                     pre = p[i].r;  
  55.                 }  
  56.             }  
  57.         }  
  58.         cout<<"Case "<<count++<<": "<<re<<endl;  
  59.     }  
  60.     return 0;  
  61. }  


区间均取最少2个点的问题:

给定一个大区间[a,b],再给出几个小区间 ,找出满足下述条件的所含元素个数最少的集合的个数,该集合满足

——对于给定的每一个区间,都至少有两个不同的整数属于该集合。

本题数据规模较大,用搜索做会超时,而动态规划无从下手。考虑贪心算法。题目意思是要找一个集合,该集合中的数的个数既要少又要和所给定的所有区间都有2个点的交集。(每个区间至少有两个该集合中的数)。我们可以从所给的区间中选数,为了选尽量少的数,应该使所选的数和更多的区间有交集这就是贪心的标准。一开始将所有区间按照右端点从小到大排序。从第一个区间开始逐个向后检查,看所选出的数与所查看的区间有无交集,有两个则跳过,只有一个数相交,就从当前区间中选出最大的一个数(即右端点),若无交集,则从当前区间选出两个数,就(右端点,右端点-1),直至最后一个区间。

[cpp]  view plain copy
  1. #include<stdio.h>  
  2. #include<string.h>  
  3. #include<stdlib.h>  
  4. struct prince  
  5. {     
  6.     int left,right;//区间左右端点       
  7. }a[10000];  
  8. int n;  
  9. int result;//存放结果中的数   
  10. int cmp(const void *a,const void *b)  
  11. {     
  12.     return (*(prince *)a).right-(*(prince *)b).right;     
  13. }  
  14. int work()  
  15. {  
  16.     qsort(a+1,n,sizeof(a[0]),cmp);//按区间右端点由小到大排序   
  17.     int i;  
  18.     int a1,a2;  
  19.     a1=a[1].right-1;a2=a[1].right;result=2;  
  20.     for(i=2;i<=n;i++)      
  21.     {  
  22.         if(a[i].left<=a1&& a[i].right>=a2)continue;//完全包含的情况下 其区间的2点就是上次的2点 直接跳过  
  23.     if (a[i].left>a2 )//完全不包含  则要添加进去2点  
  24.     {  
  25.         a1=a[i].right-1;a2=a[i].right;result=result+2;  
  26.     }  
  27.     if (a[i].left>a1 && a[i].right>a2 && a[i].left<=a2)      
  28.     {  
  29.         a1=a2;a2=a[i].right;result++;}//只包含一个     
  30.     }     
  31.     return result;    
  32. }  
  33.   
  34. int main()  
  35. {  
  36.     scanf("%d",&n);  
  37.     int i;  
  38.     for(i=1;i<=n;i++)      
  39.         scanf("%d %d",&a[i].left,&a[i].right);  
  40.    printf("%d\n",work());     
  41.     return 0;  
  42. }  

Problem 1230 区间相交问题 

http://acm.fzu.edu.cn/problem.php?pid=1230

Accept: 511    Submit: 1445
Time Limit: 1000 mSec    Memory Limit : 32768 KB

 Problem Description

给定 x 轴上 n 个闭区间。去掉尽可能少的闭区间,使剩下的闭区间都不相交。

★算法设计:对于给定的 n 个闭区间,计算去掉的最少闭区间数。

 Input

对于每组输入数据,输入数据的第一行是正整数 n (1<=n<=40,000),表示闭区间数。接下来的 n 行中,每行有 2 个整数,分别表示闭区间的 2 个端点。

 Output

输出计算出的去掉的最少闭区间数。

 Sample Input

310 2015 1020 15

 Sample Output

2





 Source



思路,贪心,按右端点排序,然后从小到大选,第一个肯定要选,区间相交的情况分两种,一种是一个区间被另一个区间所包含,那么选那个区间比较小的那个,为其他区间腾出区间,另外就是不包含的话就以当前区间右端点为基准,直到有区间的左顶点大于它为止,更新当前区间右端点




[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <cstdio>  
  3. #include <algorithm>  
  4. using namespace std;  
  5. const int maxn=40002;  
  6. struct segment  
  7. {  
  8.     int begin, end;  
  9.     segment(int _b=0, int _e=0):begin(_b),end(_e){};  
  10.     inline bool operator<( const segment& ss ) const  
  11.     {//按照区间的右端点排序  
  12.         return end<ss.end || (end==ss.end)&&(begin<ss.begin);  
  13.     }  
  14.     inline void input()  
  15.     {  
  16.         scanf("%d %d",&begin, &end);  
  17.         if(begin>end)//保证左端点值不比右端点大  
  18.             begin^=end, end^=begin, begin^=end;  
  19.     }  
  20. }seg[maxn];  
  21. int main()  
  22. {  
  23.     int n;  
  24.     while(scanf("%d",&n)!=EOF)  
  25.     {  
  26.         int i, res=1, limit;  
  27.         for(i=0; i<n; i++)  
  28.             seg[i].input();  
  29.         sort(seg,seg+n);  
  30.         limit=seg[0].end;  
  31.         for(i=1; i<n; i++)  
  32.         {//seg[i].begin<=limit的所有区间都是相互相交的,因为这些区间必然有公共点limit,即某一个区间的右端点  
  33.             if(seg[i].begin>limit)  
  34.                 res++, limit=seg[i].end;  
  35.         }  
  36.         printf("%d\n",n-res);  
  37.     }  
  38.     return 0;  
  39. }  

Bus

Accepted : 63 Submit : 514
Time Limit : 1000 MS Memory Limit : 65536 KB 

题目描述

小强刚来到长沙这个大城市,发现这里有很多他老家没有的东西,其中一个就是公交车了。小强的家到学校有很多个公交站,每个公交站都有一个英文名字。小强很喜欢坐公交车,但是他有个奇怪的要求,就是公交车的上车站和下车站的英文名字必须是首字母相同的,且不在同一个站上下车,不然小强宁愿走过这个站去搭下一趟车,甚至直接走到学校。给出小强从家里到学校的之间每一个公交站的英文名字,问如果不往回走,小强最多能搭几次公交车?

输入

多组样例,每组样例第一行为非负整数n(n<=1000),表示小强家里到学校之间有n个公交站。接下来n行,每行有一个英文名字,每行不超过100字符。

输出

对于每组样例,输出最多的乘坐次数。

样例输入
4
shaoshan
erzhong
shangxia
dongmen
5
shaoshan
shangxia
ertian
erzhong
dongmen

样例输出
1
2

http://202.197.224.59/OnlineJudge2/index.php/Contest/read_problem/cid/24/pid/1151


和上面那个题一样


思路:按照题意,可以将首字母相同的站当成一个区间。题目就转换成了,从x个区间中,怎样选择让不想交的区间最多。思路是贪心。

代码:

[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include <algorithm>  
  3. using namespace std;  
  4. struct st  
  5. {  
  6.     int s,e;  
  7. }a[1000002];  
  8. char ch[1002][103];  
  9. bool cmp(st a,st b)  
  10. {  
  11.     if(a.e==b.e)  
  12.         return a.s<b.s;  
  13.     return a.e<b.e;  
  14. }  
  15. int main()  
  16. {  
  17.     int i,j,n;  
  18.     int tm;  
  19.     int w;  
  20.     while(scanf("%d",&n)!=EOF)  
  21.     {  
  22.         tm=0;  
  23.         int s;  
  24.         for(i=0;i<n;i++)  
  25.         {  
  26.             scanf("%s",ch[i]);  
  27.             for(j=0;j<i;j++)  
  28.             {  
  29.                 if(ch[i][0]==ch[j][0])  
  30.                 {  
  31.                     a[tm].s=j;  
  32.                     a[tm++].e=i;  
  33.                 }  
  34.             }  
  35.         }  
  36.         if(tm>0)  
  37.         {  
  38.             s=1;  
  39.             w=a[0].e;  
  40.             for(i=1;i<tm;i++)  
  41.             {  
  42.                 if(a[i].s>w)  
  43.                 {  
  44.                     s++;  
  45.                     w=a[i].e;  
  46.                 }  
  47.             }  
  48.             printf("%d\n",s);  
  49.         }  
  50.         else  
  51.             printf("0\n");  
  52.     }  
  53.     return 0;  
  54. }  

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值