关于区间相关这类问题,基本思路都是排序,然后找交集或者找合集。首先要做的是讨论区间之间的包含情况,都画出图,决定是end还是start排序。
1. 最多不相交区间
【题目描述】
n个开区间 ( a i , b i ) (a_i,b_i) (ai,bi)。选择尽量多个区间,使得这些区间两两不相交。
终点排序
- 按照终点从小到大排序,pre初始化为排序后第一个区间(end最小,对于end相同的多个区间,选择start最大的区间)
加入结果集合 - 遍历区间集合,将pre与遍历到的当前区间cur比较,将会出现以下三种情况(因为已经按照end排序)
- 根据情况决定是否更新pre,然后将新的pre加入结果集合
可以看出,只有第一种情况需要更新pre=cur
如果每次遍历一个cur都要比较是三种情况中的哪一个比较麻烦,根据图示可以看出,只有第一种情况我们需要更新pre并加入结果结合,然后cur++;其余两种都不需要处理,只需要cur++
我们只要直接定位到第一种情况对应的区间即可:
满足pre.end<cur.start,就更新pre=cur,然后将pre加入结果集合
sort(intervals,key=lambda x:x[1])
pre=intervals[0]
for cur in intervals:
if pre[1]<cur[0]:
pre=cur
ans.append(pre)
起点和终点排序的不同之处
,终点排序可以直接确定要加入结果集合的pre,即确定了pre就可以直接加入结果集合;起点排序需要先根据情况确定pre和cur是否加入结果,然后再更新pre。
起点排序
- 起点从小到大排序,pre初始化为排序后的第一个区间
- 然后遍历集合的过程中,将pre与遍历到的当前区间cur 比较,会出现以下三种情况
- 根据不同的情况,决定是否pre更新,以及pre和cur哪一个区间加入结果集合
pre(蓝色)和cur(绿色)的结果有以下三种:
case1:pre和cur都加入结果集合,pre=cur
case2:cur加入结果集合,pre=cur
case3:pre加入结果集合,pre不更新
sort(intervals,key=lambda x:x[0])
pre=intervals[0]
for cur in intervals:
if pre[1]<cur[0]:
ans.append(pre)
ans.append(cur)
pre=cur
elif pre[0]<cur[0] and pre[1]>cur[1]:
ans.append(cur)
pre=cur
else:
ans.append(pre)
【例题】
2.区间选点
【题目描述】
数轴上有n个闭区间 [ a i , b i ] [a_i,b_i] [ai,bi]。取尽量少的点,使得每个区间内都至少有一个点(不同区间内含的点可以是同一个)
终点排序
- end排序,pre初始化为第一个(end最小)
- 点就取pre[1]的位置,cur用来遍历集合
- 只要是和pre有交集的区间,就都包含点了,直到遍历找到pre.end<cur.start,pre=cur,重复2(每次更新pre就确定一个点)
- 直到cur 遍历整个集合
起点排序
其实就是找尽量多个区间的交集,或者说,确定一个小区间([a,a])被尽量多的区间包含,如何确定?在遍历过程中不断找交集,缩小交集
- start排序,pre初始化为第一个(start最小)
- 用cur遍历集合
- 当pre合cur有交集的时候,更新pre区间(缩小) :pre[0]=max(pre[0] , cur[0]) pre[1]=min(pre[1] , cur[1])
- 当pre和cur没有交集的时候,点取在pre内即可(一般就缩小到一个点了pre[0]=pre[1]),然后更新pre=cur
- 直到遍历整个区间
3.区间覆盖
【问题描述】
给定一个长度为m的区间,再给出n条线段的起点和终点(注意这里是闭区间), 求最少使用多少条线段可以将整个区间完全覆盖 样例:
区间长度8,可选的覆盖线段[2,6],[1,4],[3,6],[3,7],[6,8],[2,4],[3,5]
起点排序
- 将每一个区间按照左端点递增顺序排列,排完序后为[1,4],[2,4],[2,6],[3,5], [3,6],[3,7],[6,8]
- 设置一个变量表示已经覆盖到的区域cover
- 再剩下的线段中找出所有左端点小于等于当前
已经覆盖到的区域的右端点的线段中,右端点最大的线段在加入:
cur遍历集合,找cover.end<cur.start所有满足的cur,选择end最大的cur,更新cover=cover U cur