[JSOI2016]炸弹攻击1
模拟退火。
和板子题(那道题是不是板子尚且有争论,但它是我拿来学习模拟退火的)一样是在平面内找点,但不同的是这道题不具有什么单调性(这对平面二分或三分是致命的,但对模拟退火构不成太大的影响)。就还是一样,卡了半天总结出了把时间从温度抢过来而给精度和次数的结论,多退火几次总是没有问题的,初温设置为2000就够没必要太大,末温1e-9甚至就够了。还有就是eps的问题以及每栋房子是一个有半径的圆,第一次写的时候把这茬事情给忘了(竟然还有40分……)。
code:
#include<bits/stdc++.h>
//#define feyn
const int N=1010;
const double eps=1e-6;
const double down=0.997;
using namespace std;
inline void read(int &wh){
wh=0;int f=1;char w=getchar();
while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
while(w>='0'&&w<='9'){wh=wh*10+w-'0';w=getchar();}
wh*=f;return;
}
struct node{
int x,y,r;
}a[N],b[N];
int m,n,r;
double nx,ny;
int na;
inline double poww(double wh){
return wh*wh;
}
inline double dis(double x1,double y1,double x2,double y2){
return sqrt(poww(x1-x2)+poww(y1-y2));
}
inline int f(double x,double y){
double rr=r;
for(int i=1;i<=m;i++){
double now=dis(x,y,a[i].x,a[i].y)-a[i].r;
if(now-eps<rr)rr=now-eps;
}
int num=0;
for(int i=1;i<=n;i++){
if(dis(x,y,b[i].x,b[i].y)+eps<=rr)num++;
}
return num;
}
int solve(){
double t=2000;
while(t>1e-10){
double xx=nx+(double)(rand()*2-RAND_MAX)*t;
double yy=ny+(double)(rand()*2-RAND_MAX)*t;
int num=f(xx,yy);
if(num>na)nx=xx,ny=yy,na=num;
else if(exp((na-num)/t)*RAND_MAX<rand())nx=xx,ny=yy,na=num;
t*=down;
}
return na;
}
signed main(){
#ifdef feyn
freopen("in.txt","r",stdin);
#endif
read(m);read(n);read(r);
for(int i=1;i<=m;i++){
read(a[i].x);read(a[i].y);read(a[i].r);
}
for(int i=1;i<=n;i++){
read(b[i].x);read(b[i].y);
nx+=b[i].x,ny+=b[i].y;
}
nx/=n;ny/=n;na=f(nx,ny);
int ans=0;
while((double)clock()/CLOCKS_PER_SEC<0.7)solve();
printf("%d",na);
return 0;
}
一如既往,万事胜意