贪心4.
还是熟悉的区间问题,但这次应该是要求区间的交集。
首先肯定还是排好序,不然东一个西一个不好做。
def findMinArrowShots(self, points: List[List[int]]) -> int:
points.sort(key=lambda x:x[0])
lastInterval = [-inf, inf]
res = 1
def overlaps(currentInterval):
nonlocal lastInterval
s1, e1 = lastInterval[0], lastInterval[1]
s2, e2 = currentInterval[0], currentInterval[1]
if s2 > e1:
lastInterval = currentInterval
return False
lastInterval = [max(s1,s2),min(e1,e2)]
return True
for i in points:
if not overlaps(i):
res += 1
return res
思路是保持一个最小交集,如果新区间与之前区间的最小交集不重合那就要一支新箭了。
这题把我带回了MIT6.006的课堂上。当时Devadas让学生举手回答:当两个区间重合的时候应该erase哪一个?结果学生提出的每个方案Devadas都举出了一个反例,给我看乐了。
话说回来,本题的精髓确实是局部最优如何达成,即当两个区间重合的时候应该erase哪一个。答案是:首先,按照开始位置排序整个区间数组。如果currentInterval被lastInterval包围(contain),那就erase lastInterval。不然就erase currentInterval。你能看出为什么吗?
def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
lastInterval = None
def overlap(i):
nonlocal lastInterval
if not lastInterval or lastInterval[1] <= i[0]:
return False
return True
count = 0
intervals.sort(key=lambda x:x[0])
for i in intervals:
if overlap(i):
if i[1] <= lastInterval[1]:
lastInterval = i
count += 1
else:
lastInterval = i
return count
理解题目限制:如果字母a在区间内,那么区间需要至少延伸到a的最后一次occurrence。
所以这题和之前的跳跃游戏类似,都是要动态更新区间的长度。
首先找到一个{字母:lastOccurrence}的字典,然后根据上图的逻辑更新区间长度。
def partitionLabels(self, s: str) -> List[int]:
lastOcc = {}
for i in range(len(s)-1, -1, -1):
if s[i] not in lastOcc:
lastOcc[s[i]] = i
currentmax = 0
res = []
for i in range(len(s)):
currentmax = max(currentmax, lastOcc[s[i]])
if i == currentmax:
res.append(i+1 - sum(res))
return res
如果说452是求区间的交集,那这题就是求区间的并集。思路差不多,关键还是开始时要对区间数组进行排序。
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
def overlaps(i1, i2):
if i2[0] > i1[1]:
return False
return True
def merge(i1, i2):
return [min(i1[0], i2[0]), max(i1[1], i2[1])]
res = []
temp = []
intervals.sort()
for i in intervals:
if not temp:
temp = i
continue
if overlaps(temp, i):
temp = merge(temp, i)
else:
res.append(temp)
temp = i
if temp:
res.append(temp)
return res