[几何 模拟退火 || 随机增量法] Codeforces 442E #253 (Div. 1) E. Gena and Second Distance

来源:互联网 发布:项目管理方法论 知乎 编辑:程序博客网 时间:2024/06/05 00:33

怎么都在理性愉悦啊 总算做到一道有点思路的题了

这里写图片描述

官方题解
题解里说的是什么呢
我们先二分答案Ans
然后我们考虑Ans合法的条件是存在一个圆O 他只包含其中一个特殊点
我们肯定可以挪动这个圆 使得有至少一个点在边界上
枚举点p在边界上 那么圆心O就在以p为圆心的圆C上 圆心O合法的条件是最多一个其他点到他的距离 Ans
实际上就是C的圆周上的不被两个以上以其他点为圆心相同半径的圆覆盖的点
这个可以极角排序一发 O(nlogn)
这样我们的复杂度就是O(logAns n2logn)

然后我们采用随机的思想 我们把点random_shuffle一下
一个一个点去做 把二分的过程套在里面 如果当前点在已知Ans的情况下已经不合法了 也就是他不能使答案增加 那么我们就不做一次二分 否则我们二分得出更大的Ans
这样 我们可以证明期望做O(logn)次二分 期望复杂度是 O(n2logn+nlog2nlogAns)
证明很轻易啊 因为点shuffle后 每个点答案相当于随机序列 递增子序列的长度是 O(logn)

说了这么多 但是我懒 写了个模拟退火 嘿嘿嘿

#include<cstdio>#include<cstdlib>#include<algorithm>#include<cmath>#include<ctime>using namespace std;typedef double ld;inline ld sqr(ld x) { return  x*x; }struct PP{  ld x,y;  PP(ld x=0,ld y=0):x(x),y(y){ }  friend PP operator +(PP a,PP b) { return PP(a.x+b.x,a.y+b.y); }  friend PP operator -(PP a,PP b) { return PP(a.x-b.x,a.y-b.y); }  friend PP operator *(PP a,ld b) { return PP(a.x*b,a.y*b); }  friend PP operator /(PP a,ld b) { return PP(a.x/b,a.y/b); }  friend ld det(PP a,PP b) { return a.x*b.y-a.y*b.x; }  friend ld operator *(PP a,PP b) { return a.x*b.x+a.y*b.y; }  friend ld dist(PP a,PP b){ return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y)); }  void read(){ double _x,_y; scanf("%lf%lf",&_x,&_y); x=_x; y=_y; }};const int N=2005;int n,w,h;PP p[N]; double Ans=0;inline ld F(PP a){  double min1=1e30,min2=1e30,d;  for (int i=1;i<=n;i++){    d=dist(a,p[i]);    if (d<min1){      min2=min1,min1=d;    }else      min2=min(min2,d);  }  if (min2>Ans)    Ans=max(Ans,min2);  return min2;}inline bool jud(PP p){  return p.x>=0 && p.x<=w && p.y>=0 && p.y<=h;}const double PI=acos(-1.0);inline void SA(){  PP a=PP((rand()%1000)/1000.0*(double)w,(rand()%1000)/1000.0*(double)h),b;  double ans=F(a),cur,delta;  for (double T=sqrt((double)w*w+(double)h*h);T>1e-15;T*=.995){    double ang=rand()%100/100.0*2*PI;    b=a+PP(cos(ang)*T,sin(ang)*T);    if (!jud(b)) continue;    cur=F(b); delta=cur-ans;    if (delta>0)      a=b,ans=cur;  }}int main(){  srand(time(0));  freopen("t.in","r",stdin);  freopen("t.out","w",stdout);  scanf("%d%d%d",&w,&h,&n);  for (int i=1;i<=n;i++) p[i].read();  const int Case=10;  for (int i=1;i<=Case;i++)    SA();  printf("%.15lf\n",Ans);  return 0;}
0 0
原创粉丝点击