[JSOI2016]炸弹攻击1

link

模拟退火。

和板子题(那道题是不是板子尚且有争论,但它是我拿来学习模拟退火的)一样是在平面内找点,但不同的是这道题不具有什么单调性(这对平面二分或三分是致命的,但对模拟退火构不成太大的影响)。就还是一样,卡了半天总结出了把时间从温度抢过来而给精度和次数的结论,多退火几次总是没有问题的,初温设置为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;
}
posted @ 2022-07-08 17:22  Feyn618  阅读(26)  评论(0编辑  收藏  举报