zoukankan      html  css  js  c++  java
  • 【洛谷1742】最小圆覆盖(随机增量法)

    点此看题面

    • 给定(n)个点,求这些点的最小覆盖圆。
    • (nle10^5)

    (O(n^3))的超级暴力

    首先我们需要知道一个定理,如果(P)不在点集(S)的最小覆盖圆上,则(P)必然在(Scup{P})的最小覆盖圆上。

    而众所周知三点确定一个圆,所以我们就有一个很暴力的做法。

    首先我们枚举一个点(p_i),如果它不在原本前(i-1)个点的最小覆盖圆上,就必然在当前前(i)个点的最小覆盖圆上。

    因此我们重构最小覆盖圆,由于初始只能确定这一个点在最小覆盖圆上,所以令此时的最小覆盖圆圆心为当前点,半径为(0)

    然后我们再在([1,i))中枚举点(p_j),如果它不在当前的最小覆盖圆上,同理我们可以确定它在新的最小覆盖圆上,那么我们就能确定两个点。

    所以令此时的最小覆盖圆的圆心为这两个点构成线段的中点,半径就是这两点间距离的一半。

    最后,再在([1,j))中枚举点(p_k),再度同理,如果它不在当前的最小覆盖圆上,它就在新的最小覆盖圆上。

    此时我们已经能确定最小覆盖圆上的三个点了,由于三点确定一个圆,所以肯定可以计算出当前的最小覆盖圆。

    三点确定一个圆

    假设我们已知三点(A,B,C),要求出它们的最小覆盖圆。

    众所周知,外心是中垂线的交点,所以我们只要求出(AB,AC)的中垂线,然后求出它们的交点即可。

    中垂线就是取中点作垂线,而求交点可以用面积法。

    应该是非常基础的计算几何了吧。

    随机增量法

    前面说了这么多,但复杂度似乎是非常爆炸的(O(n^3))

    然而,其实我们只要把“良心”的出题人为我们精心构造的数据给(random\_shuffle)一发,就能过了!

    为什么呢?

    依旧是利用三点确定一个圆的结论,那么(p_i)不在之前的最小覆盖圆上的概率就应该是(frac 3i),同理(p_j)不在之前的最小覆盖圆上的概率就应该是(frac 3j)

    所以说,期望下操作总次数应该是:

    [sum_{i=1}^n(frac3i imes (sum_{j=1}^ifrac3j imes j))=9n ]

    也就是说,复杂度应该是非常神奇的(O(n))

    人们常说(O(n^2))过百万,居然没想到还能(O(n^3))过十万。

    代码:(O(n))

    #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 100000
    #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 DB L() {return sqrt((*this)*(*this));}//长度
    }p[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,B;I S(Con P& a=P(),Con P& b=P()):A(a),B(b){}};
    I S MidVer(Con P& A,Con P& B) {P mid=(A+B)/2,t=B-mid;return S(mid,mid+P(t.y,-t.x));}//中垂线
    I P GetO(Con P& A,Con P& B,Con P& C)//三点确定一个圆
    {
    	S S1=MidVer(A,B),S2=MidVer(A,C);DB w1=(S1.A-S2.A)^(S2.B-S2.A),w2=(S2.B-S2.A)^(S1.B-S2.A);//计算面积
    	return S1.A+(S1.B-S1.A)*w1/(w1+w2);//求交点
    }
    int main()
    {
    	RI i,j,k;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%lf%lf",&p[i].x,&p[i].y);
    	srand(302627441),random_shuffle(p+1,p+n+1);//随机增量法
    	for(i=1;i<=n;++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 printf("%.10lf
    %.10lf %.10lf
    ",C.r,C.O.x,C.O.y),0;//输出答案
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    IHE 官方网址有用资源介绍
    HL7 标准及实现指南 必看的网址
    HL7及PIX相关的测试工具
    hl7 v2.X 版本中RSP_K23消息的构造
    hl7中V2版本的ACK消息的构造
    hl7消息中和时间有关的字段的格式
    解决方案: the selected file is a solution file but was created by a newer version of this application and cannot be opened
    wpf中为DataGrid添加checkbox支持多选全选
    hl7 V2中Message Control ID的含义及应用
    Pix mesa 自动化测试
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu1742.html
Copyright © 2011-2022 走看看