JAVA 单调区间 交集_区间覆盖全部类型及部分精选习题汇总详解(贪心策略)

本文详细探讨了三种基于贪心策略的区间问题:区间完全覆盖、最大不相交覆盖和区间选点问题。通过举例和解题过程,展示了如何利用贪心策略求解这些问题,包括对线段按照特定顺序排序并选择右端点最大者来优化覆盖。
摘要由CSDN通过智能技术生成

内容如下:

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

#include

#include

#include

usingnamespacestd;

#define Max(a,b) a>b?a:b

typedefstruct

{

ints_time;

inte_time;

}D_farmers;

D_farmers df[5001];

intn;

boolcmp(D_farmers a,D_farmers b)//起始时间的升序排序

{

returna.s_time

}

intmain()

{

ifstream fin("milk2.in");

ofstream fout("milk2.out");

inti,max1_t=0,max2_t=0,s_max,t_max;//s_max和t_max分别记录最大起始时间和结束时间

fin>>n;

for(i=0;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

{

if(t_max>=df[i].s_time&&df[i].e_time>=t_max)//前一个结束时间大于当前起始时间,更新新的截至时间,

t_max=df[i].e_time;

elseif(t_max

{

//cout<

max1_t=Max(t_max-s_max,max1_t);

//cout<

max2_t=Max(df[i].s_time-t_max,max2_t);

s_max=df[i].s_time;

t_max=df[i].e_time;

}

}

//cout<

fout<

return0;

}

以下内容复制于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……。

33e3120e04adcbeea2ea5f8e0d5432ce.png

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

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

#include 

#include 

usingnamespacestd;

structExtent

{

inta,b;

booloperator 

{

returnb  S.a;

}

}A[10002];

intmain()

{

intz,n,cnt,end;

scanf("%d",&z);

while(z--)

{

cnt = 0;

end = -1;

scanf("%d",&n);

for(inti=0;i

scanf("%d%d",&A[i].a,&A[i].b);

sort(A,A+n);

for(inti=0;i

{

if(end 

{

end = A[i].b;

cnt++;

}

}

printf("%d\n",cnt);

}

return0;

}

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.

61c30e487f3146973af4c21b2f103926.png

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轴相交的区间为一个区  这样就变成了在区间内找最少的点问题了

#include

#include

usingnamespacestd;

typedefstruct

{

doublel,r;

}in;

intcmp(constvoid*a,constvoid*b)

{

return(*(in *)a).l >= (*(in *)b).l ? 1:-1;

}

intmain()

{

intn,d,i,x,y,sw,re,count = 1;

doublepre;

in p[1000];

while(1)

{

cin>>n>>d;

if(n == 0 && d==0)break;

sw = 1;

for(i=0;i

{

cin>>x>>y;

if(d>=y&&sw==1)

{

p[i].l = x-sqrt((double)d*d - (double)y*y);

p[i].r = x+sqrt((double)d*d - (double)y*y);

}

else

{

sw = 0;

}

}

if(sw == 0)

{

cout<

continue;

}

qsort(p,n,sizeof(in),cmp);

re = 1;

pre = p[0].r;

for(i=1;i

{

if(p[i].l>pre)

{

re++;

pre = p[i].r;

}

else

{

if(p[i].r

 
 

{

pre = p[i].r;

}

}

}

cout<

}

return0;

}

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

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

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

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

#include

#include

#include

structprince

{

intleft,right;//区间左右端点

}a[10000];

intn;

intresult;//存放结果中的数

intcmp(constvoid*a,constvoid*b)

{

return(*(prince *)a).right-(*(prince *)b).right;

}

intwork()

{

qsort(a+1,n,sizeof(a[0]),cmp);//按区间右端点由小到大排序

inti;

inta1,a2;

a1=a[1].right-1;a2=a[1].right;result=2;

for(i=2;i<=n;i++)

{

if(a[i].left<=a1&& a[i].right>=a2)continue;//完全包含的情况下 其区间的2点就是上次的2点 直接跳过

if(a[i].left>a2 )//完全不包含  则要添加进去2点

{

a1=a[i].right-1;a2=a[i].right;result=result+2;

}

if(a[i].left>a1 && a[i].right>a2 && a[i].left<=a2)

{

a1=a2;a2=a[i].right;result++;}//只包含一个

}

returnresult;

}

intmain()

{

scanf("%d",&n);

inti;

for(i=1;i<=n;i++)

scanf("%d %d",&a[i].left,&a[i].right);

printf("%d\n",work());

return0;

}

Problem 1230 区间相交问题

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

Accept: 511    Submit: 1445

Time Limit: 1000 mSec    Memory Limit : 32768 KB

d2a10af27f7fce1eef153f75f846cf38.gif Problem Description

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

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

d2a10af27f7fce1eef153f75f846cf38.gif Input

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

d2a10af27f7fce1eef153f75f846cf38.gif Output

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

d2a10af27f7fce1eef153f75f846cf38.gif Sample Input

310 2015 1020 15

d2a10af27f7fce1eef153f75f846cf38.gif Sample Output

2

d2a10af27f7fce1eef153f75f846cf38.gif Source

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

#include 

#include 

#include 

usingnamespacestd;

constintmaxn=40002;

structsegment

{

intbegin, end;

segment(int_b=0,int_e=0):begin(_b),end(_e){};

inlinebooloperator

{//按照区间的右端点排序

returnend

}

inlinevoidinput()

{

scanf("%d %d",&begin, &end);

if(begin>end)//保证左端点值不比右端点大

begin^=end, end^=begin, begin^=end;

}

}seg[maxn];

intmain()

{

intn;

while(scanf("%d",&n)!=EOF)

{

inti, res=1, limit;

for(i=0; i

seg[i].input();

sort(seg,seg+n);

limit=seg[0].end;

for(i=1; i

{//seg[i].begin<=limit的所有区间都是相互相交的,因为这些区间必然有公共点limit,即某一个区间的右端点

if(seg[i].begin>limit)

res++, limit=seg[i].end;

}

printf("%d\n",n-res);

}

return0;

} 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个区间中,怎样选择让不想交的区间最多。思路是贪心。

代码:

#include 

#include 

usingnamespacestd;

structst

{

ints,e;

}a[1000002];

charch[1002][103];

boolcmp(st a,st b)

{

if(a.e==b.e)

returna.s

returna.e

}

intmain()

{

inti,j,n;

inttm;

intw;

while(scanf("%d",&n)!=EOF)

{

tm=0;

ints;

for(i=0;i

{

scanf("%s",ch[i]);

for(j=0;j

{

if(ch[i][0]==ch[j][0])

{

a[tm].s=j;

a[tm++].e=i;

}

}

}

if(tm>0)

{

s=1;

w=a[0].e;

for(i=1;i

{

if(a[i].s>w)

{

s++;

w=a[i].e;

}

}

printf("%d\n",s);

}

else

printf("0\n");

}

return0;

}

以上内容转自:https://blog.csdn.net/enjoying_science/article/details/41177531

基于贪心算法的几类区间覆盖问题:

(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],[3,5],[2,6],

[3,6],[3,7],[6,8]

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

3、贪心证明:因为需要尽量多的独立的线段,所以每个线段都尽可能的小,

对于同一右端点,左端点越大,线段长度越小。那么为什么要对右端点进行排序呢?

如果左端点进行排序,那么右端点是多少并不知道,那么每一条线段都不能对之前所有

的线段进行一个总结,那么这就明显不满足贪心的最有字结构了。

(3)区间选点问题

问题描述:

给定一个长度为m的区间,再给出n条线段和这n条线段需要满足的要求

(要求是这n条线段上至少有的被选择的点的个数),问题是整个区间内最少

选择几个点,使其满足每一条线段的要求.

样例:略

解题过程:

将每个线段按照终点坐标进行递增排序,相同终点的前点坐标从大到小排列,

一个个将其满足(每次选择的点为该条线段的右端点)

贪心证明:

要想使得剩下的线段上选择的点最少,那么就应该尽量使得已经选择了的点尽量能

在后面的线段中发挥作用,而我们是从左往右选择线段的,那么要使得选取的点能

满足后面线段的要求,那么必须是从线段的右端点开始选点,那么问题(2)一样涉及

到一个问题,如果是按照线段的左端点对线段进行排序的话,不知道右端点的话,

每一条线段都不能对之前已经操作过的所有线段进行一个总结,那么这就同样不满足

贪心算法的最优子结构性质了。

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

至少有一个点(不同区间内含的点可以是同一个)

应用例题:(貌似不是很简单。。。)

有一列整数,他的每一个数各不相同,我们不知道有多少个,但我们知道在

某些区间中至少有多少个整数,用区间(L,R,C)来描述,表示整数序列

中至少有C个整数来自子区间[L, R],若干个这样的区间,问这个整数序列的长

度最少能为多少。

区间选点算法实现:

1bb552a40c3c5731b60fa970436deac0.gif 1 #include

2 #include

3

4 using namespacestd;5

6 structline7 {8 intleft;9 intright;10 }a[100];11

12 boolcmp(line p, line q)13 {14 if(p.right !=q.right)15 return p.right q.left;17 }18

19 intmain()20 {21 intn;22 while(cin >>n)23 {24 for(int i = 0; i < n; ++i)25 cin >> a[i].left >>a[i].right;26 sort(a, a +n, cmp);27 int cnt = 0;28 int end = -1;29 for(int i = 0; i < n; ++i)30 {31 if(end >= a[i].left && end <=a[i].right)32 continue;33 else

34 {35 ++cnt;36 end =a[i].right;37 }38 }39 cout << cnt <

以上内容转自:https://www.cnblogs.com/dongsheng/archive/2013/04/19/3030444.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值