2021-12-15 每日打卡:难题精刷
写在前面
“这些事儿在熟练之后,也许就像喝口水一样平淡,但却能给初学者带来巨大的快乐,我一直觉得,能否始终保持如初学者般的热情、专注,决定了在做某件事时能走多远,能做多好。” 该系列文章由python编写,所刷题目共三个来源:之前没做出来的 ;Leetcode中等,困难难度题目; 周赛题目;某个专题的经典题目,所有代码已AC。每日1-3道,随缘剖析,希望风雨无阻,作为勉励自己坚持刷题的记录。
1610. 可见点的最大数目
- 首先把坐标和角度关系统一,这里使用极角转换:
- 使用反三角函数,范围最多只能覆盖 π \pi π,需要坐标判断
- 使用库函数
atan2
:
这个库函数的范围是 [ − π , π ] [-\pi,\pi] [−π,π],但是我们可以通过符号加2 π \pi π转换一下,最终范围如下图:
注意,这道题的难度在于一些细节:
atan2
函数的返回值是弧度制的角度,所以degree也需要改变!- 如果直接使用返回值问题,会漏掉夹角横跨二三象限的情况,也就是
−
π
-\pi
−π到
π
\pi
π这段距离是断掉的,因为此时坐标并不连续。所以我们期望把
−
π
-\pi
−π的坐标加上2
π
\pi
π使得这一块坐标连续,而检查时需要检查的是-
π
\pi
π到
π
\pi
π加上转化后的部分,所以遍历的范围一定要注意,是
还未转化的数组长度
!直接添加到数组中的原因是因为- π \pi π的位置正好在排序后放在第一位,也是接下来该排的位置。 - 可以选择滑动数组or二分法的方法快速检查:
class Solution:
def visiblePoints(self, points: List[List[int]], angle: int, location: List[int]) -> int:
sameCnt = 0
polarDegrees = []
for p in points:
# 自身位置
if p == location:
sameCnt += 1
else:
polarDegrees.append(atan2(p[1] - location[1], p[0] - location[0]))
polarDegrees.sort()
# 并不是遍历旋转检查,而是直接对数组中的数检查
n = len(polarDegrees)
polarDegrees += [deg + 2 * pi for deg in polarDegrees]
degree = angle * pi / 180
maxCnt = max((bisect_right(polarDegrees, polarDegrees[i] + degree) - i for i in range(n)), default=0)
return maxCnt + sameCnt
- 滑动窗口的方法:
class Solution:
def visiblePoints(self, points: List[List[int]], angle: int, location: List[int]) -> int:
sameCnt = 0
polarDegrees = []
for p in points:
if p == location:
sameCnt += 1
else:
polarDegrees.append(atan2(p[1] - location[1], p[0] - location[0]))
polarDegrees.sort()
n = len(polarDegrees)
polarDegrees += [deg + 2 * pi for deg in polarDegrees]
right = 0
maxCnt = 0
degree = angle * pi / 180
for i in range(n):
# right = 0,这一句放在这里会超时,相当于On2了,滑动窗口的精髓在于前面的窗口边界可以重复使用!
while polarDegrees[right] <= polarDegrees[i] + degree:
right += 1
maxCnt = max(maxCnt, right - i)
return sameCnt + maxCnt