BZOJ.2823.[AHOI2012]信號塔(最小圓覆蓋 隨機增量法)


BZOJ
洛谷


一個經典的隨機增量法,具體可以看這里,只記一下大體流程。
一個定理:如果一個點\(p\)不在點集\(S\)的最小覆蓋圓內,那么它一定在\(S\bigcup p\)的最小覆蓋圓上。
所以假設我們有了前\(i-1\)個點的最小覆蓋圓,那么只需要判斷\(i\)在不在其內,就可以確定\(i\)是否在當前最小覆蓋圓上。
算法流程:

  1. 設前\(i-1\)個點的最小覆蓋圓是\(C\),判斷第\(i\)個點是否在\(C\)內。如果是,則\(i\)個點的最小覆蓋圓也是\(C\);否則進行\(2\)
  2. 確定\(p_i\)為最小覆蓋圓上的一個點。枚舉點\(j\),判斷\(j\)是否在當前最小覆蓋圓內。如果是,跳過;否則確定\(p_j\)也是最小覆蓋圓上的一個點,圓心為線段\((p_i,p_j)\)的中點,半徑為\(\frac{dis(p_i,p_j)}{2}\),進行\(3\)
  3. 枚舉點\(k\),判斷\(k\)是否在當前最小覆蓋圓內。如果是,跳過;否則確定當前最小覆蓋圓為\((p_i,p_j,p_k)\)的外接圓。

算法的復雜度分析:(隨機數據下,)因為只需要確定三個點,\(n\)個點中每個點在圓上的概率是\(\frac3n\)
那么第一層循環的復雜度\(T_1(n)=O(n)+\sum_{i=1}^nT_2(i)\),第二層循環復雜度\(T_2(n)=O(n)+\sum_{i=1}^nT_3(i)\),第三次循環復雜度為\(T_3(n)=O(n)\)
化簡一下就可以得出算法的均攤復雜度為\(O(n)\)
注意要保證點的順序是隨機的。

具體細節:
如何求三個點\((p_i,p_j,p_k)\)的最小覆蓋圓:
就是用一個性質。。垂直平分線(中垂線)上的點到線段兩邊點的距離相同。那么求出兩條線段的垂直平分線,求個交點就行了。
垂直平分線的求法就是先求一個中點(坐標相加除以\(2\)),然后做垂線(將另一個兩點之間的向量旋轉\(90^{\circ}\))。


//16952kb   780ms
#include <cmath>
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
#define MAXIN 500000
//#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=1e6+5;

char IN[MAXIN],*SS=IN,*TT=IN;
struct Vec
{
    double x,y;
    Vec(double x=0,double y=0):x(x),y(y) {}
    Vec operator +(const Vec &a)const {return Vec(x+a.x, y+a.y);}
    Vec operator -(const Vec &a)const {return Vec(x-a.x, y-a.y);}
    Vec operator *(const double a)const {return Vec(x*a, y*a);}
    double operator *(const Vec &a)const {return x*a.y-y*a.x;}
    Vec Rotate_90()const {return Vec(y,-x);}
    double len()const {return sqrt(x*x+y*y);}
    double len2()const {return x*x+y*y;}
}p[N];
typedef Vec Point;
struct Line
{
    Point p; Vec v;
    Line(Point p,Vec v):p(p),v(v) {}
    Line PerpendicularBisector()const//垂直平分線=-=
    {
        return Line((p+p+v)*0.5,v.Rotate_90());
    }
    Point Intersection(const Line &l)const
    {
        return p+v*((l.v*(p-l.p))/(v*l.v));
    }
};

inline double read()
{
    double x=0,y=0.1,f=1;register char c=gc();
    for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
    for(;isdigit(c);x=x*10+c-48,c=gc());
    for(c=='.'&&(c=gc());isdigit(c);x+=y*(c-48),y*=0.1,c=gc());
    return x*f;
}
Point CalcCircle(const Point &a,const Point &b,const Point &c)
{
//  Line A=Line(a,b-a).PerpendicularBisector(),B=Line(a,c-a).PerpendicularBisector();
    Line A=Line((a+b)*0.5,(b-a).Rotate_90()),B=Line((a+c)*0.5,(c-a).Rotate_90());
    return A.Intersection(B);
}
void Solve(const int n)
{
    srand(330), std::random_shuffle(p+1,p+1+n);//話說這個srand不夠隨機啊= = 
    Point O=p[1]; double R=0;
    for(int i=2; i<=n; ++i)
        if((p[i]-O).len2()>R)
        {
            O=p[i], R=0;
            for(int j=1; j<i; ++j)
                if((p[j]-O).len2()>R)
                {
                    O=(p[i]+p[j])*0.5, R=(p[i]-O).len2();
                    for(int k=1; k<j; ++k)
                        if((p[k]-O).len2()>R)
                            O=CalcCircle(p[i],p[j],p[k]), R=(p[k]-O).len2();
                }
        }
    printf("%.2f %.2f %.2f\n",O.x,O.y,sqrt(R));
}

int main()
{
    int n=read();
    for(int i=1; i<=n; ++i) p[i].x=read(),p[i].y=read();
//  for(int i=1; i<=n; ++i) p[i]=(Point){read(),read()};//聲明構造函數之后再這么用,貌似。。= = 不同編譯器結果不同。。
    Solve(n);

    return 0;
}

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com