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

题目: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 answer 1 不明原因(似乎是按多组用例提交的原因)

                     Compile Error 2 编译环境选成了GCC

                     Accepted  1

 

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

 

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

 

AC Code

 

#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, int k){

    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;

}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值