【C/C++】6042. 统计圆内格点数目

613 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第24天,点击查看活动详情


题目链接:6042. 统计圆内格点数目

题目描述

给你一个二维整数数组 circles ,其中 circles[i] = [xi, yi, ri] 表示网格上圆心为 (xi, yi) 且半径为 ri 的第 i 个圆,返回出现在 至少一个 圆内的 格点数目

注意:

  • 格点 是指整数坐标对应的点。
  • 圆周上的点 也被视为出现在圆内的点。

提示:

  • 1circles.length2001 \leqslant circles.length \leqslant 200
  • circles[i].length == 3
  • 1xi,yi1001 \leqslant x_i, y_i \leqslant 100
  • 1rimin(xi,yi)1 \leqslant r_i \leqslant min(xi, yi)

示例 1:

exa-11.png

输入:circles = [[2,2,1]]
输出:5
解释:
给定的圆如上图所示。
出现在圆内的格点为 (1, 2)、(2, 1)、(2, 2)、(2, 3) 和 (3, 2),在图中用绿色标识。
像 (1, 1) 和 (1, 3) 这样用红色标识的点,并未出现在圆内。
因此,出现在至少一个圆内的格点数目是 5

示例 2:

exa-22.png

输入:circles = [[2,2,2],[3,4,1]]
输出:16
解释:
给定的圆如上图所示。
共有 16 个格点出现在至少一个圆内。
其中部分点的坐标是 (0, 2)、(2, 0)、(2, 4)、(3, 2) 和 (4, 4) 。

整理题意:

题目给出 n 个圆,每个圆给了坐标以及半径,问这些圆覆盖了网格中的多少个点。并且标注是整数坐标对应的点,另外圆周上的点也算。

解题思路分析

习惯性动作,首先观察数据范围:

  1. 圆的个数不超过 200
  2. 圆点坐标不低于 [1, 1] 且不超过 [100, 100]
  3. 半径不超过横纵坐标中最小值。 根据数据非常小,暴力完全可以做,但是我们考虑怎么暴力解决。

由于第 23 个数据范围我们可以得知圆心范围在 [1, 1][100, 100] 之间,且半径也不会超过坐标的最小值,那么我们可以推断圆的最大覆盖点的范围在 [0, 0][200, 200] 内。那我们可以遍历 [0, 0][200, 200] 内每个点,判断其是否在任意一个圆内即可。

具体实现

  • 因为存在多个圆覆盖同一个点的情况,所以我们采用 set 集合容器 来存放点集,这样就可以避免重复统计相同点的情况。
  • 一个点是否在圆内我们用点到圆心的距离是否大于半径来判断。公式:(x1x2)2+(y1y2)2r2(x_1 - x_2)^2 + (y_1 - y_2)^2 \leqslant r^2
  • 最后查询一下 set 集合中有多少个点即可。

复杂度分析

  • 时间复杂度:O(4104n)O(4 *10^4 * n),其中 41044 * 10^4 为遍历 [0, 0][200, 200] 内每个点所需的时间, n 为圆的个数。
  • 空间复杂度:O(n)O(n),其中 n 为圆的个数。

代码实现

class Solution {
public:
    int countLatticePoints(vector<vector<int>>& cir) {
        //创建存放点的集合s
        set<pair<int, int>> s;
        s.clear();
        //n为给定圆的个数
        int n = cir.size();
        //因为题目数据在100以内,所以最大半径为100,那么最大涉及到的点到200
        for(int i = 0; i <= 200; i++){
            //枚举每个[0, 0] 到 [200, 200]中的所有点
            for(int j = 0; j <= 200; j++){
                //判断该点是否在给定的圆内
                for(int k = 0; k < n; k++){
                    int x = cir[k][0];
                    int y = cir[k][1];
                    int r = cir[k][2];
                    x -= i;
                    y -= j;
                    //只需判断该点距离圆心的距离是否小于半径即可。
                    if(y * y + x * x <= r * r){
                        s.insert(make_pair(i, j));
                        break;
                    }
                }
            }
        }
        //集合s中点的个数即为答案
        return s.size();
    }
};

总结

该题我们可以巧妙的利用数据范围较小的优势,通过遍历所有可能覆盖的点来判断有多少个点被覆盖,核心在于如何判断一个点是否在圆内,因为题目给出了半径,所以我们可以直接通过判断点与圆心之间的距离和半径的关系大小即可。对于本题我们可以思考一下如果当数据范围很大的时候应该如何处理呢。


结束语

每一朵鲜花的盛开都需要经历风霜雨雪,每一把利剑的形成都需要经过千锤百炼。世上没有随随便便的成功,也没有永远的轻松安逸。不怕辛苦,不惧艰难,用心打磨,你将成就更好的自己。