• 【洛谷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;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    程序员如何开始新的工作(转)
    Dom4j和Xpath(转)
    单例模式(Singleton)Holder
    请慎用java的File#renameTo(File)方法(转)
    How to append files to a .tar archive using Apache Commons Compress?(转)
    FindWindow使用方法
    sqlplus登录、连接命令
    hdu1217Arbitrage--解题报告
    String.Split()函数
    iOS_10_tableView的简单使用_红楼十二钗
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu4586.html
Copyright © 2020-2023  润新知