模拟退火算法

参考:https://m-sea.blog.luogu.org/qian-tan-SA
思想:看当前解是否小于之前的最优解,有则接受,那局部最优的就是全局最优么?非也
在寻找到一个局部最优解时,赋予了它一个跳出去的概率,也就有更大的机会能找到全局最优解。

POJ 2420
求平面内到一堆点距离最小的那个点

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
struct Point{
	double x,y;
}p[105],cur,pre;int n;
int way[4][2]={	0,1,0,-1,1,0,-1,0};
double dist(Point a,Point b){
	return sqrt( (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double get_dist(Point a){
	double sum = 0;
	for(int i=1;i<=n;++i){
		sum+=dist(a,p[i]);
	}
	return sum;
}
#define EPS 1e-8
int main(){	
	while(~scanf("%d",&n)){
		for(int i=1;i<=n;++i){
			scanf("%lf%lf",&p[i].x,&p[i].y);
		}
		pre.x=pre.y=0;
		double t = 100;
		double best = 1e10;
		while(t>EPS){
			bool ok=1;
			while(ok){
				ok=0;
				for(int i=0;i<4;++i){
					cur.x = way[i][0]*t+pre.x;//上下左右四个方向找
					cur.y = way[i][1]*t+pre.y;
					double temp = get_dist(cur);//计算到周围所有点的距离和
					if(temp<best){//局部最优更新全局最优
						best=temp;
						pre=cur;
						ok=1;
					}
				}
			}
			t*=0.98;
		}
		printf("%.0f\n",best );
	}
	return 0;
}

poj 1379
矩形内找一个点到一堆点的最近距离最大
我们可以从矩形中心跑模拟退火
注意边界不可超出哦
很类似吧

#include <iostream>
#include <cmath>
#include <cstdio>
using namespace std;
#define ll long long
#define EPS 1e-8
struct Point{
	double x,y;
}p[1024],pre,cur;
double dist(Point a,Point b){
	return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}ll x,y,m;
double cal(Point c){
	double Min = 1e10;
	for(int i=1;i<=m;++i){
		Min=min(Min,dist(p[i],c));
	}
	return Min;
}
int way[4][2]={0,1,0,-1,1,0,-1,0};
int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		scanf("%lld%lld%lld",&x,&y,&m);
		for(int i=1;i<=m;++i){
			scanf("%lf%lf",&p[i].x,&p[i].y);
		}
		pre.x=x/2;
		pre.y=y/2;
		double t=100;
		double Max=0;
		while(t>EPS){
			bool ok=1;
			while(ok){
				ok=0;
				for(int i=0;i<4;++i){
					cur.x=way[i][0]*t+pre.x;
					cur.y=way[i][1]*t+pre.y;
					if(cur.x>x||cur.y>y||cur.x<0||cur.y<0) continue;
					double temp = cal(cur);
					if(temp>Max){
						Max=temp;
						pre=cur;
						ok=1;
					}
				}	
			}
			t*=0.98;
		}
		printf("The safest point is (%.1f, %.1f).\n",pre.x,pre.y);
	}
	return 0;
}

P1337 [JSOI2004]平衡点 / 吊打XXX
emm 玄学题+物理
根据物理的知识,当系统处于平衡状态时,系统的总能量最小
这里体现的是物体的势能显然桌子上的绳子越短 势能越低
势能=mgh
那么我们可以对所有物体的势能求和
然后就可以模拟退火乱搞了

之所以玄学 是因为= =
我调成0.99就过了。。。

#include <bits/stdc++.h>
using namespace std;
struct Point{
	double x,y,weight;
}p[1024],cur,pre;	int n;
int way[4][2]={0,1,0,-1,1,0,-1,0};
double dist(Point a,Point b){
	return sqrt( (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y) );
}
double cal(Point c){
	double sum=0;
	for(int i=1;i<=n;++i){
		sum+= dist(p[i],c)*p[i].weight;
	}
	return sum;
}
#define EPS 1e-8

int main(){
	cin>>n;
	for(int i=1;i<=n;++i){
		cin>>p[i].x>>p[i].y>>p[i].weight;
	}
	double t=100;
	double Min=1e10;
	while(t>EPS){
		bool ok=1;
		while(ok){
			ok=0;
			for(int i=0;i<4;++i){
				cur.x=way[i][0]*t+pre.x;
				cur.y=way[i][1]*t+pre.y;
				if(cur.x>10000||cur.x<-10000||cur.y>10000||cur.y<-10000) continue;
				double temp=cal(cur);
				if(temp<Min){
					Min=temp;
					pre=cur;
					ok=1;
				}
			}
		}
		t*=0.99;
	}
	printf("%.3lf %.3lf\n",cur.x,cur.y );
	return 0;
}

最小球覆盖
emm按照以前的套路失败了 原因未知 可能需要更多方向吧
我也尝试调参= =
样例随便过啊~答案是向距离最大的反方向移动emm确实有道理

#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <cstdlib>
using namespace std;
struct Point3D
{
 double x,y,z;
}p[100],pre,cur;
int n;
double x2(double x){
 return x*x;
}
double dist(Point3D a,Point3D b){
 return sqrt( x2(a.x-b.x)+x2(a.y-b.y)+x2(a.z-b.z) );
}
Point3D cal(Point3D a){
 double Max=0;
 int index=0;
 for(int i=1;i<=n;++i){
  double temp=dist(a,p[i]);
  if(temp>Max) Max=temp,index=i;
 }
 return p[index];
}
#define EPS 1e-8
int main(){
 while(~scanf("%d",&n)&&n){
  for(int i=1;i<=n;++i){
   scanf("%lf%lf%lf",&p[i].x,&p[i].y,&p[i].z);
  } 
  double T = 100;
  double Min=1e20;
  cur.x=cur.y=cur.z=0;
  while(T>EPS){
   Point3D q = cal(cur);
   double temp  = dist(cur,q);
   Min=min(Min,temp);
   cur.x += (q.x-cur.x)/temp*T;
   cur.y += (q.y-cur.y)/temp*T;
   cur.z += (q.z-cur.z)/temp*T;
   T*=0.98;
  }
  printf("%.5f\n",Min);
 }
 return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值