Problem
给定n个危险点,在一个给定矩形区域上找到一点,使得其离最近的危险点的距离最大。
n<=1000
Solution
你会发现不好二分答案,于是就可以随机化。
对于这个题目,对于每个危险点的距离就相当与形成了一个波纹一样的圆向外扩散,我们可以将危险的点理解为一个山顶。而当这些圆相遇时,就会汇在一起成为一条山谷,答案就在山谷中,这就是题目所对应的等高线地形图。
随机化30个点,设delta为每一次的步长,每次的操作就是,以此点为圆心,以这个步长为半径作一个圆,再随机化30个角度theta,得到下次的点,然后根据我们之前画的等高线地形图,我们会容易找到山谷,而且随机了30个点,所以其实也可以不以一定概率接受较差解。我们的点就会接近山谷,然后沿着山谷旁向更低的地方游走。。
那个srand里面的值,可能填你的生日或者幸运数就行,theta要迷之乘10,增大角度的离散程度吧可能是
Code
#include <cstdlib>
#include <cstdio>
#include <cmath>
using namespace std;
const double INF=1e20,eps=1e-3,pi=acos(-1.0);
int z,x,y,n,ans;
double tmp,tdis,dx,dy,delta,theta,dis[40];
struct data{
double x,y;
data(){}
data(double _x,double _y){x=_x;y=_y;}
data operator - (const data &t){return data(x-t.x,y-t.y);}
inline double dis(){return sqrt(x*x+y*y);}
}p[1010],t[40],nxt;
inline double min(double x,double y){return x<y?x:y;}
double randf(){return (rand()%1000+1)*1.0/1000.0;}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
srand();
scanf("%d",&z);
while(z--)
{
scanf("%d%d%d",&x,&y,&n);
for(int i=1;i<=n;i++) scanf("%lf%lf",&p[i].x,&p[i].y);
for(int i=1;i<=30;i++)
{
t[i].x=randf()*x;t[i].y=randf()*y;dis[i]=INF;
for(int j=1;j<=n;j++) dis[i]=min(dis[i],(t[i]-p[j]).dis());
}
delta=(1.0*(x>y?x:y))/(sqrt(n*1.0));
while(delta>eps)
{
for(int i=1;i<=30;i++)
{
t[0]=t[i];
for(int j=1;j<=30;j++)
{
theta=randf()*pi*10;
dx=delta*cos(theta);dy=delta*sin(theta);
nxt.x=t[0].x+dx;nxt.y=t[0].y+dy;tdis=INF;
if(nxt.x<0||nxt.x>x||nxt.y<0||nxt.y>y) continue;
for(int k=1;k<=n;k++) tdis=min(tdis,(nxt-p[k]).dis());
if(tdis>dis[i]){dis[i]=tdis;t[i]=nxt;}
}
}
delta*=0.8;
}
ans=0;
for(int i=1;i<=30;i++)
if(dis[i]>dis[ans])
ans=i;
printf("The safest point is (%.1lf, %.1lf).\n",t[ans].x,t[ans].y);
}
return 0;
}