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;
}

浙公网安备 33010602011771号