洛谷P4586 [FJOI2015]最小覆盖双圆问题(最小圆覆盖)

题面

传送门

前置芝士

最小圆覆盖

题解

我们按照\(x\)坐标排序,然后二分中间点,把点分成左右两边,对两边都做一个最小圆覆盖,那么半径大一点的那个就是答案了。然后对半径大的那一边继续二分就行了

然而这里显然会有一个问题……就是如果最优解中把点分成两个点集的那条直线是斜的该怎么办……

那么我们就把整个坐标系转一下好了……枚举一下偏角,然后把所有点逆时针转过这个角度……实测大概每次转\({2\pi \over 100}\),转\(100\)次差不多就可以了

还有一个我很好奇的问题,如果有三点共线的情况,那么最小圆覆盖求两条中垂线交点的时候可能会出现中垂线平行的情况显然是会\(gg\)的,所以解决办法要么是特判平行要么是加上微小扰动来避免三点共线,然而我看了看别人的似乎都没管这一点也能\(A\)……

哪位鸽鸽能告诉我这是为什么么qwq

//minamoto
#include<bits/stdc++.h>
#define R register
#define inline __inline__ __attribute__((always_inline))
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
    R int res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
double readdb()
{
    R double x=0,y=0.1,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(x=ch-'0';(ch=getc())>='0'&&ch<='9';x=x*10+ch-'0');
    for(ch=='.'&&(ch=getc());ch>='0'&&ch<='9';x+=(ch-'0')*y,y*=0.1,ch=getc());
    return x*f;
}
const int N=1005;const double an=1.0/100*acos(-1.0),eps=1e-9;
const double si=sin(an),co=cos(an);
inline int sgn(R double x){return x<-eps?-1:x>eps;}
inline double reps(){return (1.0*rand()/RAND_MAX-0.5)*eps;}
struct node{
	double x,y;
	inline node(){}
	inline node(R double xx,R double yy):x(xx),y(yy){}
	inline node operator +(const node &b)const{return node(x+b.x,y+b.y);}
	inline node operator -(const node &b)const{return node(x-b.x,y-b.y);}
	inline double operator *(const node &b)const{return x*b.y-y*b.x;}
	inline node operator *(const double &b)const{return node(x*b,y*b);}
	inline double operator ^(const node &b)const{return x*b.x+y*b.y;}
	inline double len2(){return x*x+y*y;}
	inline node rot(){return node(x*co-y*si,x*si+y*co);}
	inline node rot90(){return node(-y,x);}
	inline void shake(){x+=reps(),y+=reps();}
}p[N],st[N],o;
inline bool cmp(const node &a,const node &b){return a.x<b.x;}
struct Line{
	node p,v;
	inline Line(){}
	inline Line(R node pp,R node vv):p(pp),v(vv){}
	friend node cross(const Line &a,const Line &b){return a.p+a.v*(b.v*(b.p-a.p)/(b.v*a.v));}
};
node circle(R node a,R node b,R node c){
	return cross(Line((a+b)*0.5,(b-a).rot90()),Line((a+c)*0.5,(c-a).rot90()));
}
double query(int l1,int l2){
	if(l1>l2)return 0;
	int top=0;double r=0;o=node(0,0);
	fp(i,l1,l2)st[++top]=p[i];
	random_shuffle(st+1,st+1+top);
	fp(i,1,top)if(sgn((st[i]-o).len2()-r)>0){
		o=st[i],r=0;
		fp(j,1,i-1)if(sgn((st[j]-o).len2()-r)>0){
			o=(st[i]+st[j])*0.5,r=(st[i]-o).len2();
			fp(k,1,j-1)if(sgn((st[k]-o).len2()-r)>0)
				o=circle(st[i],st[j],st[k]),r=(st[i]-o).len2();
		}
	}
	return r;
}
int n;double res;
int main(){
	srand(20030719);
//	freopen("testdata.in","r",stdin);
	while(n=read()){
		fp(i,1,n)p[i].x=readdb(),p[i].y=readdb(),p[i].shake();
		res=1e18;
		fp(d,1,160){
			fp(i,1,n)p[i]=p[i].rot();
			sort(p+1,p+1+n,cmp);
			int l=1,r=n;
			while(l<=r){
				int mid=(l+r)>>1;
				double r1=query(1,mid),r2=query(mid+1,n);
				double ans=r1<r2?r2:r1;
				if(r1+r2-ans>res)break;
				cmin(res,ans);
				r1<r2?l=mid+1:r=mid-1;
			}
		}
		printf("%.2lf\n",sqrt(res));
	}
	return 0;
}
posted @ 2019-04-13 07:56  bztMinamoto  阅读(430)  评论(0编辑  收藏  举报
Live2D