An integer interval [a, b]
(for integers a < b
) is a set of all consecutive integers from a
to b
, including a
and b
.
Find the minimum size of a set S such that for every integer interval A in intervals
, the intersection of S with A has size at least 2.
Example 1:
Input: intervals = [[1, 3], [1, 4], [2, 5], [3, 5]]
Output: 3
Explanation:
Consider the set S = {2, 3, 4}. For each interval, there are at least 2 elements from S in the interval.
Also, there isn't a smaller size set that fulfills the above condition.
Thus, we output the size of this set, which is 3.
Example 2:
Input: intervals = [[1, 2], [2, 3], [2, 4], [4, 5]]
Output: 5
Explanation:
An example of a minimum sized set is {1, 2, 3, 4, 5}.
Note:
intervals
will have length in range[1, 3000]
.intervals[i]
will have length2
, representing some integer interval.intervals[i][j]
will be an integer in[0, 10^8]
.
Approach #1: Greedy [Accepted]
Intuition
Let's try to solve a simpler problem: what is the answer when the set intersection size is at least one?
Sort the points. Take the last interval [s, e]
, which point on this interval will be in S
? Since every other interval has start point <= s
, it is strictly better to choose s
as the start. So we can repeatedly take s
in our set S
and remove all intervals containing s
.
We will try to extend this solution to the case when we want an intersection of size two.
Algorithm
For each interval, we will perform the algorithm described above, storing a todo
multiplicity which starts at 2
. As we identify points in S
, we will subtract from these multiplicities as appropriate.
One case that is important to handle is the following: [[1, 2], [2, 3], [2, 4], [4, 5]]
. If we put 4, 5
in S
, then we put 2
in S
, when handling [2, 3]
we need to put 3
in S
, not 2
which was already put.
We can handle this case succinctly by sorting intervals [s, e]
by s
ascending, then e
descending. This makes it so that any interval encountered with the same s
has the lowest possible e
, and so it has the highest multiplicity. When at interval [s, e]
and choosing points to be included into S
, it will always be the case that the start of the interval (either s
or s, s+1
) will be unused.
思路:(1)按照start或者end来排序,(2)从排序的前面或者后面开始遍历
枚举讨论一下发现:可以按照start来排,然后从后面开始遍历;或者按照end来排,然后从前面开始遍历
对于K=2的情况,当然也是取最边上的2个数,但是因为可能已经有2个在某个区间了,这时候就需要判断要不要加上边上的数,加几个?可以有2中方法
(1)判断当前set里面有多少个数在当前interval里面
(2)每次加入一个数就更新每个interval还需要添加的个数
class Solution:
def intersectionSizeTwo(self, intervals):
"""
:type intervals: List[List[int]]
:rtype: int
"""
intervals.sort(key=lambda l: (l[0], -l[1])) # tricky sort
todo = [2]*len(intervals) # how many need to add for intersect
ret = 0
while intervals:
(s,e), t = intervals.pop(), todo.pop() # list pop from end
# greedy: add from start point s
for p in range(s, s+t):
ret += 1
for i, (s0,e0) in enumerate(intervals):
if todo[i] and p<=e0: # since sorted, p is always > e0
todo[i] -= 1
return ret
s=Solution()
print(s.intersectionSizeTwo([[1, 3], [1, 4], [2, 5], [3, 5]]))
print(s.intersectionSizeTwo([[1, 2], [2, 3], [2, 4], [4, 5]]))
查找二分优化为logN
其实排序完就只需要O(N),因为只需要维护最后加入的2个数,就可以判断当前interval满不满足条件
class Solution {
public:
int intersectionSizeTwo(vector<vector<int>>& intervals) {
sort(intervals.begin(), intervals.end(), [](vector<int>& a, vector<int>& b) {
return a[1] < b[1] || (a[1] == b[1] && a[0] > b[0]);
});
int n = intervals.size(), ans = 0, p1 = -1, p2 = -1;
for (int i = 0; i < n; i++) {
// current p1, p2 works for intervals[i]
if (intervals[i][0] <= p1) continue;
// Neither of p1, p2 works for intervals[i]
// replace p1, p2 by ending numbers
if (intervals[i][0] > p2) {
ans += 2;
p2 = intervals[i][1];
p1 = p2-1;
}
// only p2 works;
else {
ans++;
p1 = p2;
p2 = intervals[i][1];
}
}
return ans;
}
};