P5544 [JSOI2016]炸弹攻击1 【模拟退火】

洛谷 P5544 -> Click Here

题意

平面上有一些建筑和敌人,建筑为一个圆,敌人为一个点,让我们圈起来一个圆形,问最多有几个敌人在这个圆中,且圆与建筑圆没有重合部分,若敌人在所圈起来的圆的圆弧上则也算在圆中(详情请查看原题链接

思路

经典平面上求最大值问题,设初始时选择的圆心为所有敌人所组成的形状的重心(随便选一个也可以,为所谓),进行模拟退火,随机产生新的点,查看新点所能包括的敌人是否更优,随着的温度的降低,随机生成的点从离谱变得有谱起来,以接近最优解,多重复几次增大找到正解的几率

code

#include<iostream>
#include<ctime>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define REP(i,a,b) for(int i=(a);i<=(b);i++)
#define FOR(i,a,b) for(int i=(a);i<(b);i++)
#define down 0.996
#define eps 1e-15
#define N 15
#define M 1005
using namespace std;
struct build{double x,y,r;}s[N];
double a[M],b[M],sumx,sumy,ansx,ansy,R,n,m,ans;
double dis(double x,double y,double xx,double yy){
	return sqrt((x-xx)*(x-xx)+(y-yy)*(y-yy));
}
double Query(double x,double y){
	double res=0,d=R;
	REP(i,1,n) d=min(d,dis(x,y,s[i].x,s[i].y)-s[i].r);
	REP(i,1,m) if(dis(x,y,a[i],b[i])<=d) res++;
	return res;
}
void SA(){
	double T=3000;
	double nowx=ansx;
	double nowy=ansy;
	while(T>eps){
		double xx=nowx+((rand()<<1)-RAND_MAX)*T;
		double yy=nowy+((rand()<<1)-RAND_MAX)*T;
		double nowans=Query(xx,yy);
		double del=nowans-ans;
		if(del>0){
			ansx=xx;
			ansy=yy;
			nowx=xx;
			nowy=yy;
			ans=nowans;
		}else if(exp(- del/T)*RAND_MAX<rand()){
			nowx=xx;
			nowy=yy;
		}
		T*=down;
	}
}
int main(){
	srand(1e9+7);
	scanf("%lf%lf%lf",&n,&m,&R);
	REP(i,1,n) scanf("%lf%lf%lf",&s[i].x,&s[i].y,&s[i].r);
	REP(i,1,m){
		scanf("%lf%lf",&a[i],&b[i]);
		sumx+=a[i];
		sumy+=b[i];
	}
	ansx=sumx/m;
	ansy=sumy/m;
	while((double)clock()/CLOCKS_PER_SEC<=0.8) SA();
	printf("%.0lf\n",ans);
	return 0;
}
posted @ 2021-08-06 17:25  莳曳  阅读(136)  评论(0)    收藏  举报