P2533 [AHOI2012] 信号塔

题意

在一个平面上,给 \(n\) 个点,求半径最小的圆覆盖所有的点,输出圆心的位置和半径。
\(n\le10^6\)

思路

这是一个很经典的随机化?!可惜我不会,但我会退火!!!
先把凸包求出来,查询距离圆心最远的点时可以在凸包上找,这样可以快很多。初始点可以设在凸包直径的中点,据说这个点比较优。每次随机一个和温度有关的增量,去尝试更新当前解。
单次退火肯定是不行的,需要循环多次进行,可以使用卡时的方法尽量多的跑退火。

代码

调参调了好久,并抢到了最劣解,拉了之前的可见最劣解 \(8\) 秒。

#include<bits/stdc++.h>
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T&x){
        x=0;char c=getchar();bool f=0;
        while(!isdigit(c)) c=='-'?f=1:0,c=getchar();
        while(isdigit(c)) x=x*10+c-'0',c=getchar();
        f?x=-x:0;
    }
    template<typename T>
    inline void write(T x){
        if(x==0){putchar('0');return ;}
        x<0?x=-x,putchar('-'):0;short st[50],top=0;
        while(x) st[++top]=x%10,x/=10;
        while(top) putchar(st[top--]+'0');
    }
    void write(double x){printf("%lf",x);}
    inline void read(char&c){c=getchar();while(isspace(c)) c=getchar();}
    inline void write(char c){putchar(c);}
    inline void read(string&s){s.clear();char c;read(c);while(!isspace(c)&&~c) s+=c,c=getchar();}
    inline void write(string s){for(int i=0,len=s.size();i<len;i++) putchar(s[i]);}
    template<typename T>inline void write(T*x){while(*x) putchar(*(x++));}
    template<typename T,typename...T2> inline void read(T&x,T2&...y){read(x),read(y...);}
    template<typename T,typename...T2> inline void write(const T x,const T2...y){write(x),putchar(' '),write(y...),sizeof...(y)==1?putchar('\n'):0;}
}using namespace IO;
const int maxn=1000010;
const double eps=1e-12,jw=0.991;
int n,d1,d2;
double nowx,nowy,nowdis,ansx,ansy,ansdis;
struct point{
    double x,y;
    bool operator<(const point t)const{
        if(abs(x-t.x)<=eps) return y<t.y;
        return x<t.x;
    }
}a[maxn];
vector<int>hull;
bool check(point a,point b,point c){
    double val=(b.x-a.x)*(c.y-b.y)-(c.x-b.x)*(b.y-a.y);
    return val<=eps;
}
double calc_area(point a,point b,point c){return abs((b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x));}
double calc_area(int a,int b,int c){return calc_area(::a[a],::a[b],::a[c]);}
double calc_dis(point a,point b){return (b.x-a.x)*(b.x-a.x)+(b.y-a.y)*(b.y-a.y);}
double calc_dis(int a,int b){return calc_dis(::a[a],::a[b]);}
int nxt(int i){return (i+1)%hull.size();}
double query(double x=nowx,double y=nowy){double ans=0;for(int d:hull) ans=max(ans,calc_dis({x,y},a[d]));return ans;}
void find_hull(){
    hull.push_back(1);
    for(int i=2;i<=n;i++){
        while(hull.size()>=2&&check(a[hull[hull.size()-2]],a[hull[hull.size()-1]],a[i])) hull.pop_back();
        hull.push_back(i);
    }
    int lt=hull.size();
    for(int i=n-1;i>=1;i--){
        while(hull.size()>lt&&check(a[hull[hull.size()-2]],a[hull[hull.size()-1]],a[i])) hull.pop_back();
        hull.push_back(i);
    }
    hull.pop_back();
}
void find_diameter(){
    int j=1;
    double nowans=0;
    for(int i=0;i<hull.size();i++){
        while(calc_area(hull[i],hull[nxt(i)],hull[j])-calc_area(hull[i],hull[nxt(i)],hull[nxt(j)])<=eps) j=nxt(j);
        double dis=calc_dis(hull[i],hull[j]);
        if(nowans-dis<=eps) nowans=dis,d1=hull[i],d2=hull[j];
        dis=calc_dis(hull[nxt(i)],hull[j]);
        if(nowans-dis<=eps) nowans=dis,d1=hull[nxt(i)],d2=hull[j];
    }
}
void update(){if(nowdis-ansdis<=eps) ansdis=nowdis,ansx=nowx,ansy=nowy;}
void SA(){
    double Tem=3000;
    while(Tem>eps){
        double newx=nowx+(rand()*2.0/RAND_MAX-1)*Tem,newy=nowy+(rand()*2.0/RAND_MAX-1)*Tem;
        double newdis=query(newx,newy);
        if(newdis-nowdis<=eps){nowdis=newdis,nowx=newx,nowy=newy;update();}
        else if(exp(-(nowdis-newdis)/Tem)*RAND_MAX<rand()) nowdis=newdis,nowx=newx,nowy=newy;
        Tem*=jw;
    }
}
signed main(){
    srand(20120515);
    read(n);
    for(int i=1;i<=n;i++) scanf("%lf%lf",&a[i].x,&a[i].y);
    sort(a+1,a+1+n);
    find_hull();find_diameter();
    ansdis=query(ansx,ansy);
    nowx=(a[d1].x+a[d2].x)/2;
    nowy=(a[d1].y+a[d2].y)/2;
    nowdis=query();
    while(clock()<960000) SA();
    printf("%.2lf %.2lf %.2lf",ansx,ansy,sqrt(ansdis));
    return 0;
}
posted @ 2026-04-24 21:58  Link-Cut_Trees  阅读(8)  评论(0)    收藏  举报