【洛谷4586】[FJOI2015] 最小覆盖双圆问题(随机增量法)

点此看题面

  • 给定(n)个点,找出两个半径相等的圆覆盖所有点,且半径最小。
  • (nle10^3)

最小覆盖圆

必须要先去看看这道题:【洛谷1742】最小圆覆盖(随机增量法)

通过上面这题,默认你已经知道如何求最小覆盖圆了。

旋转+二分

考虑我们找一条分界线把点集划分成两部分,分别求出两部分的最小覆盖圆,那么只要能找到一个最优的划分,使得两个最小覆盖圆中较大的半径即为答案。

由于分界线可能是斜线,似乎不太好操作。

因此我们第一步是将整张图不断旋转,每次转大概(frac{pi}{100}),总有一种情况使得最优的分界线垂直于(x)轴。

然后,我们把所有点按照横坐标排序,接着通过二分,每次取中间位置作为划分线更新答案,再就看左右区间哪边最小覆盖圆半径大就往哪边走。

代码:(O(200nlogn))

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 1000
#define DB double
using namespace std;
int n;struct P
{
	DB x,y;I P(Con DB& a=0,Con DB& b=0):x(a),y(b){}
	I P operator + (Con P& o) Con {return P(x+o.x,y+o.y);}
	I P operator - (Con P& o) Con {return P(x-o.x,y-o.y);}
	I P operator * (Con DB& o) Con {return P(x*o,y*o);}
	I P operator / (Con DB& o) Con {return P(x/o,y/o);}
	I DB operator * (Con P& o) Con {return x*o.x+y*o.y;}
	I DB operator ^ (Con P& o) Con {return x*o.y-y*o.x;}
	I bool operator < (Con P& o) Con {return x<o.x;}
	I DB L() {return sqrt((*this)*(*this));}
	I P R(Con DB& g) {return P(x*cos(g)-y*sin(g),x*sin(g)+y*cos(g));}
}s[N+5],s_[N+5];
struct Cir
{
	P O;DB r;I Cir(Con P& o=P(),Con DB& x=0):O(o),r(x){}
	I bool Chk(Con P& p) {return (O-p).L()<=r;}
}C;
struct S {P A,V;I S(Con P& a=P(),Con P& v=P()):A(a),V(v){}};
I S MidVer(Con P& A,Con P& B) {return S((A+B)/2,(B-A).R(acos(-1)/2));}
I P GetO(Con P& A,Con P& B,Con P& C)//三点确定一个圆
{
	S S1=MidVer(A,B),S2=MidVer(A,C);if(fabs(S2.V^S1.V)<1e-12) return P(-1e9,-1e9);
	return S1.A+S1.V*((S2.V^(S2.A-S1.A))/(S2.V^S1.V));
}
P p[N+5];I DB Calc(CI l,CI r)//计算[l,r]中点的最小覆盖圆
{
	RI i,j,k;for(i=l;i<=r;++i) p[i-l+1]=s[i];random_shuffle(p+1,p+r-l+2),C=Cir();
	for(i=1;i<=r-l+1;++i) if(!C.Chk(p[i])) for(C=Cir(p[i],0),
		j=1;j^i;++j) if(!C.Chk(p[j])) for(C=Cir((p[i]+p[j])/2,(p[i]-p[j]).L()/2),
			k=1;k^j;++k) if(!C.Chk(p[k])) C.O=GetO(p[i],p[j],p[k]),C.r=(p[i]-C.O).L();
	return C.r;
}
int main()
{
	RI i,k,l,r,mid;DB t,L,R;srand(302627441);W(scanf("%d",&n),n)
	{
		for(i=1;i<=n;++i) scanf("%lf%lf",&s_[i].x,&s_[i].y);
		for(t=1e9,k=0;k^200;++k)//不断旋转
		{
			for(i=1;i<=n;++i) s[i]=s_[i].R(acos(-1)/100*k);sort(s+1,s+n+1);//旋转后按横坐标排序
			l=1,r=n;W(l<=r) mid=l+r>>1,(L=Calc(1,mid))<(R=Calc(mid+1,n))?l=mid+1:r=mid-1,t=min(t,max(L,R));//二分最优分界线
		}printf("%.2lf
",t);
	}return 0;
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu4586.html