📌  相关文章
📜  角度扫描(可包含在给定半径的圆中的最大点)

📅  最后修改于: 2021-04-27 18:31:16             🧑  作者: Mango

给定二维平面上的“ n”个点,找到可以被半径为“ R”的固定半径圆包围的最大点数。

注意:即使该点位于圆周上,也认为该点在圆内。

例子:

Input : R = 1
        points[] = {(6.47634, 7.69628), (5.16828 4.79915), 
                    (6.69533 6.20378)}
Output :  2
The maximum number of points are 2

Input :  R = 1
  points[] = {(6.65128, 5.47490), (6.42743, 6.26189)
   (6.35864, 4.61611), (6.59020 4.54228), (4.43967 5.70059)
   (4.38226, 5.70536), (5.50755 6.18163), (7.41971 6.13668)
   (6.71936, 3.04496), (5.61832, 4.23857), (5.99424, 4.29328)
   (5.60961, 4.32998), (6.82242, 5.79683), (5.44693, 3.82724) 
   (6.70906, 3.65736), (7.89087, 5.68000), (6.23300, 4.59530)
   (5.92401, 4.92329), (6.24168, 3.81389), (6.22671, 3.62210)}
Output : 11
The maximum number of points are 11

天真的算法

  1. 对于给定集合中的任意一对点(例如A和B),构造半径为’R’且同时接触这两个点的圆。最多可能有2个这样的圈子。正如我们在这里看到的,最大可能的圆圈是案例1(即案例2)。

    The circles with radius 'R' touching points A and B

  2. 对于每个构造的圆,请检查集合中的每个点是否位于圆内。
  3. 返回包含最大点数的圆。

时间复杂度:有n C 2对点对应,我们最多可以有2 n C 2个圆。对于每个圆,必须检查(n-2)个点。这使得幼稚算法为O(n 3 )。

角度扫描算法
通过使用角度扫描,我们可以在O(n 2 log n)中解决此问题。该算法的基本逻辑思想描述如下。

我们从给定集合中选择一个任意点P。然后,我们围绕点P旋转一个半径为R的圆。在整个旋转过程中,P都位于圆的圆周上,并且在给定的Θ值下,对圆的点数进行计数。参数θ确定旋转角度。由于半径是固定的,因此可以通过单个参数Θ来确定圆的状态。
我们还可以看到,仅当集合中的某个点进入或退出圆时,保持的计数值才会更改。

The single parameter Θ controls the orientation of circle

在给定的图中,C1是Θ= 0的圆,C2是当我们以Θ的一般值旋转圆时构造的圆。

此后,问题就减少到如何保持计数值上。
对于除P(例如Q)之外的任何给定点,我们可以轻松地计算出进入圆的Θ的值(设为α)和离开圆的Θ的值(设β)。
我们有定义为下的角度A和B,

  • A是PQ和X轴之间的角度。
  • B是PC和PQ之间的角度,其中C是圆心。

A = tan^{-1}  \frac{(P.y - Q.y)}{(P.x-Q.x)} \\\\ B = cos^{-1}  \frac{d}{2R}

其中,x和y表示点的坐标,“ d”是P和Q之间的距离。

现在,从图中我们可以看到,

α = A-B 
β = A+B

(注意:所有角度都相对于X轴。因此,它变为“ AB”而不是“ BA”)。

当Q进入圆圈时

当Q退出圆时

我们可以计算除P以外的所有点的角度A和B。找到这些角度后,我们对其进行排序,然后以递增的顺序遍历它们。现在我们维护一个计数器,该计数器告诉我们在特定时刻圆内有多少点。
仅当点进入或退出圆时,计数才会改变。如果找到进入角,则将计数器增加1,如果找到出口角,则将计数器减少1。使用标志可以轻松实现检查角度是进入还是退出。
如此进行,计数器始终会为我们提供特定状态下圆内点数的有效值。

重要说明:不必考虑’d’> 2R的点,因为永远不会进入或退出圆。

角度扫描算法可以描述为:

  1. 计算每对n C 2个点之间的距离并将其存储。
  2. 对于任意点(例如P),使用getPointsInside()函数获取可以绕P旋转的圆内最大的点数。
  3. 返回的所有值的最大值将是最终答案。

在下面的C++实现中已经描述了该算法。

// C++ program to find the maximum number of
// points that can be enclosed by a fixed-radius
// circle
#include 
using namespace std;
  
const int MAX_POINTS = 500;
  
// complex class which is available in STL has
// been used to implement points. This helps to
// ensure greater functionality easily
typedef complex Point;
  
Point arr[MAX_POINTS];
double dis[MAX_POINTS][MAX_POINTS];
  
// This function returns the maximum points that
// can lie inside the circle of radius 'r' being
// rotated about point 'i'
bool mycompare(pair A, pair B)
{
    if(A.firstB.first)
        return false;
    else
        return (A.second==1);
}
int getPointsInside(int i, double r, int n)
{
    // This vector stores alpha and beta and flag
    // is marked true for alpha and false for beta
    vector > angles;
  
    for (int j=0; j >::iterator it;
    for (it=angles.begin(); it!=angles.end(); ++it)
    {
        // entry angle
        if ((*it).second)
            count++;
  
        // exit angle
        else
            count--;
  
        if (count > res)
            res = count;
    }
  
    return res;
}
  
// Returns count of maximum points that can lie
// in a circle of radius r.
int maxPoints(Point arr[], int n, int r)
{
    // dis array stores the distance between every
    // pair of points
    for (int i=0; i

输出 :

The maximum number of points are: 2

时间复杂度:我们将n点称为函数getPointsInside()。此函数在“ n-1”个点上起作用,对于这些点,我们获得矢量“角度”的2 *(n-1)大小(一个入射角和一个出射角)。现在,此“角度”向量已排序并遍历,这使getPointsInside()函数的复杂度等于O(nlogn)。这使得角度扫描算法为O(n 2 log n)。

相关资源:使用stl中提供的复杂类来实现几何问题的解决方案。
http://codeforces.com/blog/entry/22175