参考: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;
}