线段覆盖问题(贪心)
2025-08-27 10:26:27

0. NOTE

注意“线段相交”的定义,[1,2]和[3,4]算不算相交(结合实际问题)

1. 求最多互不相交的线段数

例题:luogu P1803

思路:

线段右端点从小到大排序,维护右端点,依次遍历每条线段,左端点大于(或大于等于)目前维护的右端点则答案+1更新右端点。

2. 求线段总覆盖长度

例题:luogu P2082

思路:

左端点从小到大排序,遍历线段,记录左端点,维护右端点,若线段左端点小于等于右端点,右端点取max,否则累加长度,记录新的左端点。

注:按此思路容易解决线段合并问题(最少可以看做几个合并后的互不相交线段),即luogu P2434

3. 选择最少的线段覆盖给定区间

例题:luogu P1668

思路:

采用贪心算法,线段按左端点升序排列,维护右端点,若线段左端点小于等于右端点,右端点取max,到不能遍历时ans++,更新右端点

注意事项

  1. 例题中[1,2]和[3,4]相邻
  2. 如果找不到左端点小于等于右端点要break,不然死循环
  3. right>=end后要及时break,否则ans大于答案

本题还有dp做法,后续补充

4. 任选最少点使每条线段内有点(点可重复使用)

例题:luogu U248666 leetcode 452

思路:

复用1即可,选则这些线段右端点

5.第四类升级:每条线段内点数量有要求

例题:luogu P1250
思路:顺着4的思路,先将线段按右端点升序排序,之后遍历每条线段,从右端点开始种够数量,依次类推
建议使用数组维护已经选取的点(暴力遍历即可)

6.给定线段和点,取若干点使得最多线段被匹配

一个点只能和一条线段匹配
例题:luogu P2887

思路一:

我们考虑给定的一个线段,如果一个点大于它的左端点,那么这个点越小“越有利”(越可能被这条线段覆盖)。那么,从贪心的角度来说,我们应该在右端左边的点中选择一个尽可能大的,这样(大于左端点的)剩下的点对于其他线段是有利的(为了保证有利性,要求剩下的点也要在其他线段左端的右边。由此我们确定了线段按左端点降序排序,点降序排序;
暴力遍历线段,选择满足要求的点中最大的;

思路二:

与思路一对称,线段按右端点升序,点升序排列也可以

也可以这样思考:朴素地想法是先尽量满足“中间的”线段,这是由左端点降序或者右端点升序所绝定的。然后在再考虑要选择靠近哪个方向的点,实际上那个如果已经确定了一个边界,点越靠近那个方向越有利,那么在贪心的时候就要采取反过来的策略(先来的先挑选不太好的情况)

7. 4的对偶,点固定,线长度固定,选最少线使点全被覆盖

例题:SDUT 1751

思路

一个很自然地想法是以点为中心,线长度一半为臂长展开成线段,线段中心变换为点,由此又转化为问题4

升级1:一维变二维

例题:luogu P1325
转化为要求在x轴上的线段即可(注意开double,用eps,注意-1的情况)

升级2:线的数量固定,反求线段最小长度

例题:luogu P6174
二分答案即可

8.线段分解问题(调度问题)

将线段分解为最少组,每组内都不能重叠

Warning

这个题目不能逐个用问题1的思路解决,要按左端点升序排序! 可以考察这个样例:[2,3],[2,4],[4,8],[6,7]

例题: luogu P2859
思路1:左端点升序排序(谁先来谁先进组),用数组S维护每一个组的最右端点,对于每条线段,扫描S找到任意一个可以不重叠的组,进组,找不到的话新建一个组。复杂度$\mathcal{O}(n^2)$
思路2:在思路1的基础上,用小根堆维护每个组中线段的最右点,尝试安排新来的线段在堆顶所表示的组中。不行的话新建一个组,复杂度$\mathcal{O}(nlogn)$
注:如果只要求求最少要分几个组,不要求求出分组方式,可以考虑用前缀和。