double 最大_LeetCode 1610—可见点的最大数目

关键字:计算几何,滑动窗口

归航return:LeetCode 460—LFU 缓存设计​zhuanlan.zhihu.com
zhihu-card-default.svg
归航return:(Trivial)LeetCode 1354——多次求和构造目标数组​zhuanlan.zhihu.com
f575ebad7cdb43708eb65febd36c2afa.png

Problem

You are given an array points, an integer angle, and your location, where location = [posx, posy] and points[i] = [xi, yi] both denote integral coordinates on the X-Y plane.

Initially, you are facing directly east from your position. You cannot move from your position, but you can rotate. In other words, posx and posy cannot be changed. Your field of view in degrees is represented by angle, determining how wide you can see from any given view direction. Let d be the amount in degrees that you rotate counterclockwise. Then, your field of view is the inclusive range of angles [d - angle/2, d + angle/2].

You can see some set of points if, for each point, the angle formed by the point, your position, and the immediate east direction from your position is in your field of view.

There can be multiple points at one coordinate. There may be points at your location, and you can always see these points regardless of your rotation. Points do not obstruct your vision to other points.

Return the maximum number of points you can see.

Example 1:

facdff6c38627174290361bee7c2168a.png
Input: points = [[2,1],[2,2],[3,3]], angle = 90, location = [1,1]
Output: 3
Explanation: The shaded region represents your field of view. All points can be made visible in your field of view, including [3,3] even though [2,2] is in front and in the same line of sight.

Example 2:

Input: points = [[2,1],[2,2],[3,4],[1,1]], angle = 90, location = [1,1]
Output: 4
Explanation: All points can be made visible in your field of view, including the one at your location.

Example 3:

529aa62fbdca26dc335b667b0c18b36e.png
Input: points = [[1,0],[2,1]], angle = 13, location = [1,1]
Output: 1
Explanation: You can only see one of the two points, as shown above.

Constraints:

  • 1 <= points.length <= 10**5
  • points[i].length == 2
  • location.length == 2
  • 0 <= angle < 360
  • 0 <= posx, posy, xi, yi <= 10**9
1610. 可见点的最大数目 - 力扣(LeetCode)​leetcode-cn.com

Solution

这道题目饶有趣味。在 LeetCode 周赛中,这道题目的难度是 medium,但是在比赛结束之后数小时,难度被调整成了 hard 难度。这道题目是一个典型的计算几何问题,约束范围是

equation?tex=%5Cleft%5Bcur+-+%5Cfrac%7Bd%7D%7B2%7D%2C+cur+%2B+%5Cfrac%7Bd%7D%7B2%7D%5Cright%5D ,要求最大的覆盖个数。

首先,自然是要求出这个点的角度。根据高中的解析几何和三角函数知识,我们知道,这里实际上就是将极坐标和直角坐标进行互换的过程,也就是:

equation?tex=%5Cleft%5C%7B+%5Cbegin%7Baligned%7D+%26+x+%3D+%5Crho+%5Ccos%5Ctheta%5C%5C+%26+y+%3D+%5Crho+%5Csin%5Ctheta%5C%5C+%5Cend%7Baligned%7D+%5Cright.%5C%5C

其中

equation?tex=%5Crho 是到原点的长度(非负数),而
equation?tex=%5Ctheta 则是从
equation?tex=x 轴正半轴逆时针旋转的角度。一种特殊情况:当点位于原点的时候,
equation?tex=%5Crho+%3D+0 ,而
equation?tex=%5Ctheta 则是未定义的。这一点尤为重要,因为我们本题会涉及到这个问题。我们本题仅仅关心
equation?tex=%5Ctheta ,针对这个问题,
C++Java 均提供了库函数,称作 atan2(double y, double x) 或者 Math.atan2(double y, double x),得到的结果位于
equation?tex=%5Cleft%5B-%5Cpi%2C+%5Cpi%5Cright%5D 之间。当然,由于浮点运算的特性,这里一定存在误差。

相关的文档可以参考 C 的这里 C++ 的这里 Java 的这里 和 Python 的这里。由于我们这里的坐标差都是有限的,因此唯一的 edge case 就是两者都是 0。在这种情况下,由于我们的 0 的得到的过程是两个 int 相减然后转换成 double 型,因此这里的浮点数一定是 atan2(+0.0, +0.0)。针对此,C++ 的结果是多种可能(即未定义),而 Java 则是规定是 +0.0. Python 的文档中没有明确指出特殊情况的定义,但由于 Python 的底层是 C 语言,因此猜测就是按照上述 C 语言的文档进行特殊情况的结果。

  • If x and y are both zero, domain error does not occur
  • If x and y are both zero, range error does not occur either
  • If y is zero, pole error does not occur
  • If y is ±0 and x is negative or -0, ±π is returned
  • If y is ±0 and x is positive or +0, ±0 is returned
Cppreference(C++)
  • If the first argument is positive zero and the second argument is positive, or the first argument is positive and finite and the second argument is positive infinity, then the result is positive zero.
Oracle Java Docs(Java)

然而,我们自己一定能看到自己,因此这种情况一定能看到,这是一种边界情况。

另外,值得注意的是,由于 atan2 函数的值域是

equation?tex=%5Cleft%5B-%5Cpi%2C+%5Cpi%5Cright%5D ,而我们关心的是在单位圆上的角度的差,因此这里需要借用复数的幅角主值的概念来理解这个函数的结果。那么,怎么做到不漏掉角度差计算上大于阈值但实际上单位圆上的角度差小于阈值呢?我们想到了 LeetCode 503 的做法,就是多循环一次。由于圆的角度是
equation?tex=2%5Cpi 弧度,因此我们需要同时加入两个角度,一个是
atan2 的结果,一个是 atan2 + 2pi 的结果,这样就能保证不会漏掉了。

在这样加入完毕之后,对所有的角度进行排序,就转化成了很简单的最长子数组,且子数组的最大值减去最小值不超过预先给定的值的问题了,这个问题的解答也很简单,双指针即可。维护 [left, right] 的闭区间表示当前区间,如果符合要求就 ++right,否则 ++left,直到 right 超出合法的坐标范围为止。

最后别忘了,题目给的角度是角度值,需要乘以

equation?tex=%5Cfrac%7B%5Cpi%7D%7B180%7D 来得到弧度值,另外 C++ 没有提供
equation?tex=%5Cpi 的预先定义,因此需要使用一个函数来获得,方法也很简单:
equation?tex=%5Cpi+%3D+%5Carccos%5Cleft%28-1%5Cright%29 ,这是因为
equation?tex=%5Ccos%5Cleft%28%5Cpi%5Cright%29+%3D+-1 ,且
equation?tex=%5Cpi%5Cin%5Cleft%5B0%2C+%5Cpi%5Cright%5D 。最终代码如下:

Java:

class Solution {
    public int visiblePoints(List<List<Integer>> points, int angle, List<Integer> location) {
        final int pointCnt = points.size();
        List<Double> angles = new ArrayList<>();
        int extraAns = 0;
        for (int i = 0; i < pointCnt; ++i){
            int deltaY = points.get(i).get(1) - location.get(1);
            int deltaX = points.get(i).get(0) - location.get(0);
            if (deltaY == 0 && deltaX == 0){
                ++extraAns;
                continue;
            }
            double curAngle = Math.atan2(deltaY, deltaX);
            angles.add(curAngle);
            angles.add(curAngle + 2 * Math.PI);
        }
        Collections.sort(angles);
        int ans = 0;
        int left = 0;
        int right = 0;
        double maxAngle = angle * Math.PI / 180.0;
        while (true){
            if (right == angles.size()){
                return ans + extraAns;
            }
            if (angles.get(right) - angles.get(left) <= maxAngle){
                ans = Math.max(ans, right - left + 1);
                ++right;
            }
            else{
                ++left;
            }
        }
    }
}

C++ :

class Solution {
public:
    int visiblePoints(vector<vector<int>>& points, int angle, vector<int>& location) {
        int extraCnt = 0;
        vector<double> angles;
        const double PI = acos(-1);
        for (const vector<int>& point : points){
            int deltaY = point[1] - location[1];
            int deltaX = point[0] - location[0];
            if (deltaX == 0 && deltaY == 0){
                ++extraCnt;
                continue;
            }
            double curAngle = atan2(deltaY, deltaX);
            angles.emplace_back(curAngle);
            angles.emplace_back(curAngle + 2 * PI);
        }
        sort(angles.begin(), angles.end());
        double threshold = angle * PI / 180;
        int left = 0;
        int right = 0;
        int ans = 0;
        while (true){
            if (right == angles.size()){
                return ans + extraCnt;
            }
            if (angles[right] - angles[left] <= threshold){
                ans = max(ans, right - left + 1);
                ++right;
            }
            else{
                ++left;
            }
        }
    }
};

C:

int compare(const void* x, const void* y){
    double a = *(double*)x;
    double b = *(double*)y;
    return (b < a) - (a < b);
}
int visiblePoints(int** points, int pointsSize, int* pointsColSize, int angle, int* location, int locationSize){
    double angles[2 * pointsSize];
    const double PI = acos(-1);
    int angleCnt = 0;
    int extraCnt = 0;
    for (int i = 0; i < pointsSize; ++i){
        int deltaY = points[i][1] - location[1];
        int deltaX = points[i][0] - location[0];
        if (deltaX == 0 && deltaY == 0){
            ++extraCnt;
            continue;
        }
        double curAngle = atan2(deltaY, deltaX);
        angles[angleCnt++] = curAngle;
        angles[angleCnt++] = curAngle + 2 * PI;
    }
    qsort(angles, angleCnt, sizeof(double), compare);
    int left = 0;
    int right = 0;
    int ans = 0;
    double threshold = angle * PI / 180;
    while (1){
        if (right == angleCnt){
            return ans + extraCnt;
        }
        if (angles[right] - angles[left] <= threshold){
            int tmp = right - left + 1;
            ans = tmp > ans ? tmp : ans;
            ++right;
        }
        else{
            ++left;
        }
    }
}

Python3:

import math
class Solution:
    def visiblePoints(self, points: List[List[int]], angle: int, location: List[int]) -> int:
        angles = []
        extraAns = 0
        for i in range(len(points)):
            deltaY = points[i][1] - location[1]
            deltaX = points[i][0] - location[0]
            if not deltaY and not deltaX:
                extraAns += 1
                continue
            curAngle = math.atan2(deltaY, deltaX)
            angles.append(curAngle)
            angles.append(curAngle + 2 * math.pi)
        angles.sort()
        threshold = angle * math.pi / 180
        left = 0
        right = 0
        ans = 0
        while True:
            if right == len(angles):
                return ans + extraAns
            if angles[right] - angles[left] <= threshold:
                ans = max(ans, right - left + 1)
                right += 1
            else:
                left += 1

这道题目我总共错了两次,第一个错误就是没有考虑到自摸的情况,第二个则是没有考虑到循环超出 atan2 范围的情况。特别注意,这道题目并不能在阈值上加上一个小常数,这会导致在 108/109 个 testcase 上出错。然后在我撰写题解的时候,LeetCode 更新了测试用例,加上了三个全部自摸的 test case,这个时候上述的 angles 数组就是空的了,因此这个时候必须直接首先判断 right 是否超出下标范围。

时间复杂度:

equation?tex=O%5Cleft%28N%5Clog+N%5Cright%29 ,来源于排序;

空间复杂度:

equation?tex=O%5Cleft%28N%5Cright%29 ,来源于额外存放角度的数组。

EOF。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ava实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),可运行高分资源 Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现
C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。下面详细介绍C语言的基本概念和语法。 1. 变量和数据类型 在C语言中,变量用于存储数据,数据类型用于定义变量的类型和范围。C语言支持多种数据类型,包括基本数据类型(如int、float、char等)和复合数据类型(如结构体、联合等)。 2. 运算符 C语言中常用的运算符包括算术运算符(如+、、、/等)、关系运算符(如==、!=、、=、<、<=等)、逻辑运算符(如&&、||、!等)。此外,还有位运算符(如&、|、^等)和指针运算符(如、等)。 3. 控制结构 C语言中常用的控制结构包括if语句、循环语句(如for、while等)和switch语句。通过这些控制结构,可以实现程序的分支、循环和多路选择等功能。 4. 函数 函数是C语言中用于封装代码的单元,可以实现代码的复用和模块化。C语言中定义函数使用关键字“void”或返回值类型(如int、float等),并通过“{”和“}”括起来的代码块来实现函数的功能。 5. 指针 指针是C语言中用于存储变量地址的变量。通过指针,可以实现对内存的间接访问和修改。C语言中定义指针使用星号()符号,指向数组、字符串和结构体等数据结构时,还需要注意数组名和字符串常量的特殊性质。 6. 数组和字符串 数组是C语言中用于存储同类型数据的结构,可以通过索引访问和修改数组中的元素。字符串是C语言中用于存储文本数据的特殊类型,通常以字符串常量的形式出现,用双引号("...")括起来,末尾自动添加'\0'字符。 7. 结构体和联合 结构体和联合是C语言中用于存储不同类型数据的复合数据类型。结构体由多个成员组成,每个成员可以是不同的数据类型;联合由多个变量组成,它们共用同一块内存空间。通过结构体和联合,可以实现数据的封装和抽象。 8. 文件操作 C语言中通过文件操作函数(如fopen、fclose、fread、fwrite等)实现对文件的读写操作。文件操作函数通常返回文件指针,用于表示打开的文件。通过文件指针,可以进行文件的定位、读写等操作。 总之,C语言是一种功能强大、灵活高效的编程语言,广泛应用于各种领域。掌握C语言的基本语法和数据结构,可以为编程学习和实践打下坚实的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值