圆形靶内的最大飞镖数量

题目

1453. 圆形靶内的最大飞镖数量

墙壁上挂着一个圆形的飞镖靶。现在请你蒙着眼睛向靶上投掷飞镖。
投掷到墙上的飞镖用二维平面上的点坐标数组表示。飞镖靶的半径为 r 。
请返回能够落在 任意 半径为 r 的圆形靶内或靶上的最大飞镖数。

数据规模:点的个数N<=100,r<=5000

对题目进行解析,就是要找一个圆心,使得以该圆心为中心、r为半径的圆能够覆盖(在圆内或者圆上)更多的给定的点。求能覆盖的最多的点的数目。

思路

由于圆心是在二维坐标平面,是无限可能的,所以不能直接用暴力的方法。

换一种思路,如果找到这个圆(这个圆能覆盖最多的点),那么一定满足至少有两个点在这个圆上。用反证法可以证明:
如果有0个点在这个圆上,可以通过移动圆心,使得有1个点在圆上,并且被覆盖的点不会变少。
如果有1个点在这个圆上,可以通过移动圆心,使得有2个点在圆上,并且被覆盖的点不会变少。可以通过固定这个点在圆上,然后进行旋转。

所以遍历两两的点,找到他们的圆心,求这个圆心覆盖的点的个数。

从圆上两个点找到圆心的公式是:
Paper.Paper_工具.15

实现代码如下:

class Solution {
    double precision=1e-10;
    public int numPoints(int[][] points, int r) {
        int n=points.length;
        int max=1;
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                if(i==j){
                    continue;
                }
                double[] center=getCenter(i,j,points,r);
                if(center==null){
                    continue;
                }
                int cnt=getCnt(center,r,points);
                max=Math.max(max,cnt);
            }
        }
        return max;
    }

    private boolean onOrIn(int[] p,double[] center,int r){
        //发现有一个5.000000000000001
        return getDist(new double[]{p[0],p[1]},center)-r<=precision;
    }

    private int getCnt(double[] center,int r,int[][] points){
        int res=0;
        for(int[] p:points){
            if(onOrIn(p,center,r)){
                res++;
            }
        }
        return res;
    }
    private double[] getCenter(int i,int j,int[][] points,int r){
        double[] p0=new double[]{points[i][0],points[i][1]};
        double[] p1=new double[]{points[j][0],points[j][1]};
        double dist=getDist(p0,p1);
        if(dist>2*r){
            return null;
        }
        double a=dist/2;
        double h=Math.sqrt(r*r-a*a);
        double[] OM=new double[]{(p0[0]+p1[0])/2.0,(p0[1]+p1[1])/2.0};
        double[] MC=new double[]{p0[1]-p1[1],-(p0[0]-p1[0])};
        double factor=h/getDist(MC,new double[]{0,0});
        MC[0]*=factor;
        MC[1]*=factor;
        return new double[]{OM[0]+MC[0],OM[1]+MC[1]};
    }

    private double getDist(double[] p1,double[] p2){
        double x=p1[0]-p2[0];
        double y=p1[1]-p2[1];
        return Math.sqrt(x*x+y*y);
    }
}

还有另一种叫做Angular Sweep的算法,以后有空再看看。

参考:
关于为什么一定会有两个点在圆上
Angular Sweep
Angular Sweep

posted @ 2020-05-31 21:39  Fanny123  阅读(207)  评论(0编辑  收藏  举报