lp1337 JSOI2004 平衡点 or 吊打XXX

首先我们认识一种算法——爬山算法。这种算法总是贪心地接受更优秀的解。
但是,我们知道,并不是所有的解都是一个单峰函数。因此我们有了非常具有魔力的模拟退火算法。
我们想到一种随机化的优化方案,就是对于一个新的解,如果是更优的,就一定接受它;否则,有概率去接受它。
然而这样并不是特别优的,因为如果概率固定,那么它很大意义上还是在瞎JB跳。我们有一种被称为「模拟退火」的算法。我们定义一个全局变量T,令瞎JB跳的概率是\(e^{\frac{\Delta ans}{T}}\),然后我们令\(T\)不断降低,使得最后的答案趋于稳定。
我们相信,只要T足够大、尝试的次数足够多,那么总是能找到最优解。
然而,我们必须明白的是,模拟退火本质上是一种以时间换正确性的算法,因此要如何在时间和正确性之间取得平衡,就成为一个很困难的问题了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<ctime>
#define RD() (T*(rand()*2-RAND_MAX))
using namespace std;

typedef double db;
typedef long double ldb;
const int N=1005;
const ldb dlT=0.97;
const ldb eps=1E-14;
db x[N],y[N],w[N];
int n;
inline ldb calc(db X,db Y){
	db dx,dy,rt=0;
	for(int i=1;i<=n;++i){
		dx=x[i]-X,dy=y[i]-Y;
		rt+=sqrt(dx*dx+dy*dy)*w[i];
	}
	return rt;
}
void init(){
	scanf("%d",&n);
	db smx=0,smy=0;
	int tms=100;
	for(int i=1;i<=n;++i){
		scanf("%lf%lf%lf",x+i,y+i,w+i);
		smx+=x[i],smy+=y[i];
	}
	smx/=n,smy/=n;
	db ans,bst,x0,y0,x1,y1,nw;
	ans=bst=calc(smx,smy);
	srand(time(0));
	while(tms--){
		ans=bst,x0=smx,y0=smy;
		for(db T=100000;T>eps;T*=dlT){
			x1=x0+RD(),y1=y0+RD();
			nw=calc(x1,y1);
//			printf("%lf %lf\n",x1,y1);
			if(nw<bst){
				bst=nw;
				smx=x1,smy=y1;
			}
			if(nw<ans||(exp((ans-nw)/T)>(long double)rand()/RAND_MAX)){
				ans=nw;
				x0=x1,y0=y1;
			}
		}
	}
	printf("%.3lf %.3lf\n",smx,smy);
}
int main(){
	init();
	return 0;
}

发表评论

您的电子邮箱地址不会被公开。