最小圆覆盖  随机增量算法 衡阳八…

来源:互联网 发布:毒品网络 百度云 编辑:程序博客网 时间:2024/06/08 06:35

题目:http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=1337

 

题目大意: 给定平面上的N(N< 100000)个点,求一个最小的圆将所有的点覆盖。

 

考查点:最小圆覆盖的随机化增量算法。

思路:  在增量算法求最小圆覆盖的算法中,我们需要确定第i个点是否在当前圆中,

           如果不在,我们就要更新当前圆,由于第i个点必在更新后的圆中,因此需

从前i个点中选择一个点p,由点ip确定一个圆,然后枚举i前的其他点,利用张角法看其它点是否在圆中,这样就可以求出由第i个点确定的包含前i个点的圆。但时间复杂度很高,O(n^3),不能ac此题。当时不是每个i都会去更新当前圆,只有i不在当前圆内,才需要更新,又,i不在当前圆内的概率为3/i,因此,如果我们一开始先将数据随机化,则时间复杂度就变为了( (i * 3/i * i ^ 2)) = 3*n^2; 这样的时间复杂度依旧不可以。观察前面的过程,当我们确定了i在圆上的时候,求前i个点构成的圆又使用了张角法,时间复杂度n^2,如果我们求前i个点构成的圆也是用随机增量法,那么时间复杂度就可以进一步降低(i * 3/i * i * 3/i)=9*n

这样时间复杂度降到了线性时间复杂度,可以解决本题。

这个过程我认为需要注意两个细节

1:在由ij确定了圆以后,当添加k时,不会出现钝角三角形的情况,如果有钝角三角形,那么k点是不会在圆外的。

2:再由ijk三点确定圆的时候不会有ijk共线的情况,即可以用行列式来解二元一次方程组。

 

提交情况:wrong answer1 不明原因(似乎是按多组用例提交的原因)

                    CompileError 2 编译环境选成了GCC

                   Accepted 1

 

收获:学会了最小圆覆盖的随机化算法,也是自己第一次用随机化的思想做题,人品很好,0msAC,领悟了随机化的魅力;  此外也学会了如何用当前时间做种子来产生随机数

 

经验:提交题目要选对编译环境

 

ACCode

 

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#include<math.h>

#include<time.h>

 

#define MAXN 200

 

struct NODE{

   double x, y;

};

 

struct ROUND{

   double r, x, y;

};

NODE point[MAXN];

ROUND now;

 

void swap(int i, int j){

   NODE t = point[i];

   point[i] = point[j];

   point[j] = t;

}

 

 

double Get_X(int i, int j, int k){

   if(k == -1)

      return (point[i].x + point[j].x ) / 2;

   else{

      double a1, a2, b1, b2, c1, c2;

      a1 = 2 *(point[j].x - point[i].x);

      b1 = 2 *(point[j].y - point[i].y);

      c1 = point[j].x * point[j].x + point[j].y * point[j].y - point[i].x* point[i].x - point[i].y * point[i].y;

 

      a2 = 2 *(point[k].x - point[i].x);

      b2 = 2 *(point[k].y - point[i].y);

      c2 = point[k].x * point[k].x + point[k].y * point[k].y - point[i].x* point[i].x - point[i].y * point[i].y;

      

      return (c1 * b2 - c2 * b1) / (a1 * b2 - a2 * b1);

   }

}//ijk三点确定一个圆,k ==-1时是指用两点确定一个圆, 这是求圆心横坐标

 

double Get_Y(int i, int j, int k){

   if(k == -1)

      return (point[i].y + point[j].y ) / 2;

   else{

      double a1, a2, b1, b2, c1, c2;

      a1 = 2 *(point[j].x - point[i].x);

      b1 = 2 *(point[j].y - point[i].y);

      c1 = point[j].x * point[j].x + point[j].y * point[j].y - point[i].x* point[i].x - point[i].y * point[i].y;

 

      a2 = 2 *(point[k].x - point[i].x);

      b2 = 2 *(point[k].y - point[i].y);

      c2 = point[k].x * point[k].x + point[k].y * point[k].y - point[i].x* point[i].x - point[i].y * point[i].y;

      

      return (a1 * c2 - a2 * c1) / (a1 * b2 - a2 * b1);

   }

}//求圆心纵坐标

 

double Get_R(double x, double y, intk){

   return sqrt((x - point[k].x) * (x - point[k].x) + (y - point[k].y)* (y - point[k].y));

}//利用圆心和圆上一点求半径

 

int out(int k){

   return sqrt((point[k].x - now.x) * (point[k].x - now.x) +(point[k].y - now.y) * (point[k].y - now.y)) > now.r;

}//判断点k在不在当前圆上

 

int main(){

   int n, m, i, j, k;

   scanf("%d", &n);

      for(i = 0; i < n; i ++) scanf("%lf %lf",&point[i].x,&point[i].y);

      srand((unsigned)time(NULL));//有当前时间产生随机化种子

      for(i = 0; i < n; i ++) swap(i, rand() % n);// 将输入数据随机化

      now.r = 0;

      if(n > 1){

          now. x = Get_X(0, 1, -1);

          now. y = Get_Y(0, 1, -1);

          now. r = Get_R(now.x, now.y, 0);

      }//由前两个点确定一个圆

      for(i = 2; i < n; i ++)

          if( out(i) ){//若第i个点在圆外

             now. r= 0; 

             now. x = point[i].x;

             now. y = point[i].y; // 由第i个点确定一个圆

             for(j = i - 1; j >= 0; j --)

                 if(out(j)){// j点不在由i构成的圆内,则由ij确定一个圆

                now. x = Get_X(i, j, -1);

                 now. y = Get_Y(i, j, -1);

                 now. r = Get_R(now.x, now.y, i);

                 for(k = j + 1; k < i; k ++)

                    if( out(k) ){//ji间的点不再ij围城的圆内,则有ijk确定圆,这里ijk三点一定是锐角三角形。

                        now.x = Get_X(i, j, k);

                        now.y = Get_Y(i, j, k);

                        now. r = Get_R(now.x, now.y, i);

                    }

             }

      }

   printf("%.3lf\n", now. r); //输出半径

   return 0;

}