首页 > 代码库 > Ural 1520 Empire Strikes Back(模拟退火)

Ural 1520 Empire Strikes Back(模拟退火)

最近研究了下模拟退火,首先戳这里>_<顾研08年集训队论文,讲的非常详细

首先随机20个点作为候选解,以此扩展来确定距离工厂最近的点的最远的距离是多少。但是有需要注意到扩展点在边界的情况,因为边界是圆所以很难继续扩展,如论文中提到的两种情况。1.是两工厂垂直平分线与边界交点2.一个工厂的圆与边界相切。单独判断即可。

其中有个很蛋疼的地方,我之前距离用了很多次sqrt,T成狗,比较距离时只需比较距离平方,答案最后再开根即可

代码:https://github.com/mlz000/Ural/blob/master/1502(%E6%A8%A1%E6%8B%9F%E9%80%80%E7%81%AB).cpp

#include<iostream>//模拟退火
#include<cstring>
#include<cstdio>
#include<cmath>
#include<ctime>
#include<algorithm>
using namespace std;
#define sqr(x)	((x)*(x))
const int N=305;
const double Pi=acos(-1.0),eps=1e-9,inf=1e10;
int n;
double r;
struct Point{
	double x,y,d;
}a[N],b[25],tmp,tmp1,tmp2;
double dis(double x1,double y1,double x2,double y2){
	return sqr(x1-x2)+sqr(y1-y2);
}
int dcmp(double x){
	if(x<eps)	return -1;
	return x>eps;
}
void calc(Point &p){
	p.d=inf;
	if(dcmp(sqr(p.x)+sqr(p.y)-r*r)>0){
		p.d=0;
		return;
	}
	for(int i=1;i<=n;++i)	p.d=min(p.d,dis(p.x,p.y,a[i].x,a[i].y));
}
int main(){
	while(~scanf("%d%lf",&n,&r)){
		for(int i=1;i<=n;++i)	scanf("%lf%lf",&a[i].x,&a[i].y);
		for(int i=1;i<=20;++i){
			double len=(rand()%100)/100.0*r;
			double ang=(rand()%100)/100.0*2.0*Pi;
			b[i].x=len*cos(ang);
			b[i].y=len*sin(ang);
			calc(b[i]);
		}
		double delta=r*2/sqrt(20.0);
		for(;delta>1e-7;delta*=0.6)
			for(int i=1;i<=20;++i)
				for(int j=1;j<=30;++j){
					double ang=(rand()%100)/100.0*2.0*Pi;
					tmp.x=b[i].x+delta*cos(ang);
					tmp.y=b[i].y+delta*sin(ang);
					calc(tmp);
					if(tmp.d>b[i].d)	b[i]=tmp;
				}
		double ans=0.0;
		for(int i=1;i<=20;++i)	ans=max(ans,b[i].d);
		for(int i=1;i<=n;++i)
			for(int j=i+1;j<=n;++j){
				double A,B,C,d,s,xx,yy;
				A=2*(a[i].x-a[j].x),B=2*(a[i].y-a[j].y),C=sqr(a[j].x)+sqr(a[j].y)-sqr(a[i].x)-sqr(a[i].y);
				s=A*A+B*B;
				d=r*r*s-sqr(C);//d=d*(A^2+B^2)
				if(d+eps<0)	continue;
				if(d<eps)	d=0;
				else d=sqrt(d);
				if(s==0)	continue;
				xx=-A*C;
				yy=-B*C;
				tmp1.x=(xx+B*d)/s,tmp1.y=(yy-A*d)/s;
				tmp2.x=(xx-B*d)/s,tmp2.y=(yy+A*d)/s;
				calc(tmp1);
				ans=max(ans,tmp1.d);
				calc(tmp2);
				ans=max(ans,tmp2.d);
			}
		for(int i=1;i<=n;++i){
			double d=sqrt(sqr(a[i].x)+sqr(a[i].y));
			if(dcmp(d)<=0)	continue;
			tmp.x=a[i].x*r/d;
			tmp.y=a[i].y*r/d;
			calc(tmp);
			ans=max(ans,tmp.d);
			tmp.x=-tmp.x;
			tmp.y=-tmp.y;
			calc(tmp);
			ans=max(ans,tmp.d);
		}
		printf("%.8lf\n",sqrt(ans));
	}
	return 0;
}