Loading

模拟退火

模拟退火

模拟退火是一种 著名的 玄学的随机化算法,其建立在物理中退火过程的基础上

其时间复杂度为 \(O(\text{时限})\) (因为要跑满确保正确性) ,正确概率为 \(\text{(参数优秀程度+阳寿)}\%\)

通常人们使用造数据+手动二分调参的方式来提高正确率

这种算法是 \(oier\) 在比赛中骗分的不二选择

一般的套路是小数据跑暴力,大数据贪心+模拟退火

用于卡时:

//MAX_TIME为自定义时间上线 (单位:秒)
while((double)clock/CLOCKS_PER_SEC<MAX_TIME) SimulateAnneal

UVA10228 A Star not a Tree?

#include<bits/stdc++.h>
using namespace std;

#define down 0.97

const int N=1e5+5;

inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}

int n;
double x[N],y[N];
double ansx,ansy,ans;

//cal函数因题而异,是用于求解某一点的答案
inline double cal(double u,double v){
	double res=0;
	for(int i=1;i<=n;++i)
		res+=sqrt((u-x[i])*(u-x[i])+(v-y[i])*(v-y[i]));
	return res;
}

inline void SimulateAnneal(){
	double t=3000,mt=1e-10;//t指初始温度,mt指温度最低值
	double nowx=ansx,nowy=ansy;//初始化
	for(;t>=mt;t*=down){//t每次*down表示降温
		double nx=nowx+(rand()*2-RAND_MAX)*t;//常规随机数据
		double ny=nowy+(rand()*2-RAND_MAX)*t;
		double nows=cal(nx,ny);//计算当前随机出来的数据的答案
		double delta=nows-ans;//记录差值
		if(delta<0) ansx=nx,ansy=ny,nowx=nx,nowy=ny,ans=nows;//若当前答案更优,则更新
		else if(exp(-delta/t)*RAND_MAX>rand()) nowx=nx,nowy=ny;//否则小概率概率更新,注意所有模拟退火都是这样判断的
	}
}

signed main(){
	srand(time(0));
	int T;
	cin>>T;
	for(int i=1;i<=T;++i){
		cin>>n;
		ans=1e9,ansx=ansy=0;
		for(int i=1;i<=n;++i){
			cin>>x[i]>>y[i];
			ansx+=x[i],ansy+=y[i];
		}
		ansx/=n,ansy/=n;//初始答案
		int B=50;
		while(B--) SimulateAnneal();//跑多次模拟退火,确保答案足够准确
		cout<<round(ans)<<endl;
		if(i!=T) cout<<endl;
	}
}
posted @ 2022-07-17 20:22  Into_qwq  阅读(33)  评论(0编辑  收藏  举报