题面:
题解:
求最小圆覆盖。
随机增量法,期望复杂度O(n)。
先将所有的点随机打乱。
定理1: 如果点p不在集合S的最小覆盖圆内,则p一定在S∪{p}的最小覆盖圆上。
根据这个定理,我们可以分三个for确定前i个点的最小覆盖圆。
1.令前i-1个点的最小覆盖圆为C
2.如果第i个点在C内(包括边界),则前i个点的最小覆盖圆也是C
3.如果不在,那么第i个点一定在前i个点的最小覆盖圆上,接着确定前i−1个点中还有哪两个在最小覆盖圆上。因此,设当前圆心为Pi,半径为0,做固定了第i个点的前i个点的最小圆覆盖。
4.固定了一个点–去找第二个点:不停地在范围内找到第一个不在当前最小圆上的点Pj ,当前圆心为(Pi+Pj)/2半径为1/2 | Pi Pj | 做固定了两个点的,前 j 个点外加第 i 个点的最小圆覆盖。
5.固定了两个点–去找第三个点:不停地在范围内找到第一个不在当前最小圆上的点Pk,当前圆为Pi , Pj , Pk 的外接圆。
用random_shuffle随机打乱会wa两个点。。。
真感谢知乎上见到的O(n)随机数列。
从后往前遍历,对于每一个 p [ i ],随机与它前面的一个数交换。
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<map>
#include<vector>
#define ll long long
#define llu unsigned ll
using namespace std;
const double eps=1e-8;
const double dnf=1e20;
const double pi=acos(-1.0);
const int maxp=100100;
//浮点型数值是否为0
int sgn(double x)
{
if(abs(x)<eps) return 0;
if(x<0) return -1;
return 1;
}
//返回x的平方
double sqr(double x)
{
return x*x;
}
//二维点
struct Point
{
double x,y;
Point(){
}
Point(double xx,double yy)
{
x=xx,y=yy;
}
//输入输出
void input(void)
{
scanf("%lf%lf",&x,&y);
}
void output(void)
{
printf("%.2f %.2f\n",x,y);
}
//重载比较运算符
bool operator == (const Point &b) const
{
return sgn(x-b.x)==0&&sgn(y-b.y)==0;
}
bool operator < (const Point &b) const
{
if(sgn(x-b.x)!=0) return x<b.x;
else return sgn(y-b.y)<0;
}
//重载加减乘除
Point operator + (const Point &b) const
{
return Point(x+b.x,y+b.y);
}
Point operator - (const Point &b) const
{
return Point(x-b.x,y-b.y);
}
Point operator * (const double &k) const
{
return Point(x*k,y*k);
}
Point operator / (const double &k) const
{
return Point(x/k,y/k);
}
//叉乘,叉积
double operator ^ (const Point &b) const
{
return x*b.y-y*b.x;
}
//点乘,点积
double operator * (const Point &b) const
{
return x*b.x+y*b.y;
}
//长度
double len(void)
{
return hypot(x,y);
}
//长度的平方
double len2(void)
{
return x*x+y*y;
}
//两点距离
double dis(const Point &b) const
{
return hypot(x-b.x,y-b.y);
}
//计算pa,pb的夹角,就是这个点看a,b形成的角度
double rad(const Point &a,const Point &b)const
{
Point p=*this;
return abs(atan2(abs((a-p)^(b-p)),(a-p)*(b-p)));
}
//化向量长度为r
Point turn_to_r(double r)
{
double l=len();
if(!sgn(l)) return *this;
r/=l;
return Point(x*r,y*r);
}
//逆时针转90
Point turn_left(void)
{
return Point(-y,x);
}
//顺时针转90
Point turn_right(void)
{
return Point(y,-x);
}
//绕p点逆时针转angle
Point turn_p_angle(const Point &p,double angle)
{
Point v=(*this)-p;
double c=cos(angle),s=sin(angle);
return Point(p.x+v.x*c-v.y*s,p.y+v.x*s+v.y*c);
}
};
//****************************************************************
//****************************************************************
struct Line
{
Point s,e;
Line(){
}
//两点
Line(const Point ss,const Point ee)
{
s=ss,e=ee;
}
//点斜
Line(const Point p,const double angle)
{
s=p;
if(sgn(angle-pi/2)==0)
e=(s+Point(0,1));
else e=(s+Point(1,tan(angle)));
}
//ax+by+c=0
Line(double a,double b,double c)
{
if(sgn(a)==0)
{
s=Point(0,-c/b);
e=Point(1,-c/b);
}
else if(sgn(b)==0)
{
s=Point(-c/a,0);
e=Point(-c/a,1);
}
else
{
s=Point(0,-c/b);
e=Point(1,(-c-a)/b);
}
}
void input(void)
{
s.input();
e.input();
}
void output(void)
{
s.output();
e.output();
}
//两点应该是由s——>e,
//由x负向指向x正向。
void adjust(void)
{
if(e<s) swap(s,e);
}
//判断两线段是否重合
bool operator == (Line b)
{
adjust(),b.adjust();
return (s==b.s)&&(e==b.e);
}
//求线段长度
double len(void)
{
return s.dis(e);
}
//返回直线倾斜角
//0<=angle<pi
double angle(void)
{
double k=atan2(e.y-s.y,e.x-s.x);
if(sgn(k)<0) k+=pi;
if(sgn(k-pi)==0) k-=pi;
return k;
}
//点和直线的关系
//1 在左侧 (在s——>e的逆时针侧)
//2 在右侧 (在s——>e的顺时针侧)
//3 在直线上
int relation(const Point &p)
{
int c=sgn((p-s)^(e-s));
if(c<0) return 1;
else if(c>0) return 2;
else return 3;
}
//点是否在线段上
bool p_is_on_seg(const Point &p)
{
return sgn((p-s)^(e-s))==0&&sgn((p-s)*(p-e))<=0;
}
//两向量平行,对应直线平行或者重合
bool parallel(const Line &v)
{
return sgn((e-s)^(v.e-v.s))==0;
}
//两线段相交判断
//2 规范相交
//1 非规范相交
//0 不相交
int seg_cross_seg(const Line &v)
{
int d1=sgn((e-s)^(v