题目地址:
https://leetcode.com/problems/set-intersection-size-at-least-two/
给定 n n n个闭区间,形如 [ a , b ] [a,b] [a,b],其中 a < b a<b a<b且都为非负整数,这些区间只含整数。要求选若干点,使得每个区间都含那些点中的至少两个点。问至少要选多少个点。
思路是贪心。先按右端点从小到大排序,直觉上每次选最右边的两个点能覆盖住最多的区间。算法如下:
1、开两个变量
x
,
y
x,y
x,y,
x
<
y
x<y
x<y,表示当前选择的最右边的两个点的位置,初始化为
−
1
-1
−1(因为所有区间都不含负数);
2、遍历区间,如果当前区间
A
A
A左端点大于
y
y
y,那么必须新选两个点了,选该区间的右端点
A
[
1
]
A[1]
A[1]和
A
[
1
]
−
1
A[1]-1
A[1]−1这两个点;
3、如果当前区间
A
[
0
]
=
y
A[0]=y
A[0]=y,那么只需要多选一个点
A
[
1
]
A[1]
A[1]即可;
4、如果
x
<
A
[
0
]
<
y
x<A[0]<y
x<A[0]<y,也是只需要多选一个点,但是要看一下
A
[
1
]
A[1]
A[1],如果
A
[
1
]
=
y
A[1]=y
A[1]=y的话,那么选
A
[
1
]
−
1
A[1]-1
A[1]−1,否则选
A
[
1
]
A[1]
A[1]。
5、遍历的同时对选了多少个点计数。
算法正确性证明:
首先贪心解确实能覆盖所有区间,所以贪心解选的点数大于等于最优解。如果某个最优解不是贪心解,找到贪心解里选了但是最优解里没选的点:
1、上面的
2
2
2,对于某个区间,其左端点大于了其左边所有区间的右端点,因为贪心解会选这个区间的最右边两个点,所以最优解必然不是选的这两个点,那么可以调整过去,最优解的那两个点可以覆盖的区间,贪心解的那两个点也能覆盖,所以调整后仍然是最优解;
2、上面的
3
3
3和
4
4
4可以类似证明。
代码如下:
import java.util.Arrays;
public class Solution {
public int intersectionSizeTwo(int[][] intervals) {
Arrays.sort(intervals, (i1, i2) -> Integer.compare(i1[1], i2[1]));
// last2 < last1
int res = 0, last1 = -1, last2 = -1;
for (int[] interval : intervals) {
if (interval[0] > last1) {
last2 = interval[1] - 1;
last1 = interval[1];
res += 2;
} else if (interval[0] == last1 || interval[0] > last2) {
if (interval[1] > last1) {
last2 = last1;
last1 = interval[1];
} else {
last2 = last1 - 1;
}
res++;
}
}
return res;
}
}
时间复杂度 O ( n log n ) O(n\log n) O(nlogn),空间 O ( 1 ) O(1) O(1)。