【力扣roadmap】1610. 可见点的最大数目

13 阅读2分钟

题目描述

image.png

分析

几何题,做得少,多学习学习。 (我先说我看题解这之前的想法,我可以遍历自旋角度360,然后再依次遍历点,看其是否在视野中。这其实不包对,因为题目没说自旋角度必须是整数,而且这样的复杂度在1e7的水平,力扣估计很难过掉。)

这个题目的主要做法是转成固定窗口极差的滑动窗口问题。 我们直接将题目给的所有点平移-location,这样我们之间身处极点,然后通过atan2计算出每个点p的极角(atan2相比atan好处在于,可以自动判断所在象限,但(0,0)未定义,需要额外处理)。 转化成极角(都是弧度制,取值范围[-pi,+pi]),进行排序。我们只需要看看当窗口的极差固定后,窗口中最多能包含多少元素。 有一点值得注意,普通的滑动窗口是在线性的数组进行,我们这里是一个环,而且题目要求最大视野可以是2pi,所以我们给每个点的极角加上2pi并且拼在原列表后面,这样再计算窗口元素的时候就满足首尾相接的要求了。

代码

class Solution:
    def visiblePoints(self, points: List[List[int]], angle: int, location: List[int]) -> int:
        same = 0 
        p = []
        for x,y in points :
            if x == location[0] and y == location[1] :
                same += 1 
            else :
                p.append(atan2(y-location[1],x-location[0]))
        p.sort() 
        # 滑动窗口 -pi ~ +pi 
        p += [d + 2 * pi for d in p]  # 每个都加上2pi是因为视野最大有2pi的大小
        p = list(map(degrees,p))
        vis = 0 
        l = 0 
        for r in range(len(p)) :
            while l < r and p[r]-p[l] > angle :
                l += 1 
            # [l,r]是合法区间
            vis = max(vis, r - l + 1) 
        return vis + same 

# ---------------
class Solution:
    def visiblePoints(self, points: List[List[int]], angle: int, location: List[int]) -> int:
        same = 0 
        p = []
        for x,y in points :
            if x == location[0] and y == location[1] :
                same += 1 
            else :
                p.append(atan2(y-location[1],x-location[0]))
        p.sort() 

        # 滑动窗口 -pi ~ +pi 
        p += [d + 2 * pi for d in p]
        vis = 0 
        l = 0 
        field = angle * pi / 180 
        for r in range(len(p)) :
            while l < r and p[r]-p[l] > field :
                l += 1 

            # [l,r]是合法区间
            vis = max(vis, r - l + 1) 
        
        return vis + same