模板模拟退火 费马点以及最小球覆盖

Posted xiaobuxie

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了模板模拟退火 费马点以及最小球覆盖相关的知识,希望对你有一定的参考价值。

最近学了一波模拟退火。个人感觉,就是随机算法,然后我们的目标点,一开始温度T高的时候会移动的剧烈,T小的时候移动的缓和(所以这就是为什么每一次移动的距离都是乘T)。然后真正的模拟退火是如果当前的tem比ans优,那就毫不犹豫地接受,否则则以一定概率接受。也就是那个exp(dE/T)> rand 那个。

然后还有爬山算法,就是只会一直找更优解,不接受差解,具体就是在模拟退火基础上,一直找最优解,找不到就降温(所以会陷入局部最优解的坑)。在网上嫖了一份代码(https://blog.csdn.net/just_sort/article/details/51648958)

技术图片
 1 while(t>eps)
 2     
 3         bool fuck = 1;
 4         while(fuck)
 5         
 6             fuck = 0;
 7             for(int i=0; i<4; i++)
 8             
 9                 z.x = s.x+dir[i][0]*t;
10                 z.y = s.y+dir[i][1]*t;
11                 double tmp = getSum(p,n,z);
12                 if(ans>tmp)
13                 
14                     ans = tmp;
15                     s   = z;
16                     fuck= 1;
17                 
18             
19         
20         t*=delta;
21     
View Code

 

 

求费马点:poj2420

题目链接:https://vjudge.net/problem/POJ-2420

一开始就以坐标平均点作为起始点,然后移动的时候虽然可以无脑的向四周移动,但是我们可以这样看,求出每一个点和当前点的dx,dy,然后肯定是当前点朝dx和dy方向移动。这样会更好

(这个是带上一定概率接受更差解的)

技术图片
 1 #include<iostream>
 2 #include<cmath>
 3 #include<cstdio>
 4 #include<ctime>
 5 #include<algorithm>
 6 using namespace std;
 7 #define eps 1e-6
 8 int n;
 9 const int N = 109;
10 struct Point
11     double x,y;
12 p[N],now;
13 double dis(Point a,Point b)
14     return sqrt( (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y - b.y));
15 
16 double getsum(Point u)
17     double res = 0;
18     for(int i = 1;i<=n;++i)
19         res += dis(p[i],u);
20     
21     return res;
22 
23 int main()
24     scanf("%d",&n);
25     for(int i = 1;i<=n;++i)
26         scanf("%lf %lf",&p[i].x,&p[i].y);
27         now.x += p[i].x;
28         now.y += p[i].y;
29     
30     now.x /= n; now.y /= n;
31     double ans = getsum(now);
32     double T = 100;
33     srand(23333);
34     while(T > eps)
35         double dx = 0,dy = 0;
36         for(int i = 1;i<=n;++i)
37             dx += (p[i].x - now.x)/dis(now,p[i]);
38             dy += (p[i].y - now.y)/dis(now,p[i]);
39         
40         Point next = (Point)now.x + dx*T, now.y + dy*T;
41         double tem = getsum(next);
42         if(tem < ans)
43             ans = tem;
44             now = next;
45         
46         else if(exp( (ans - tem) / T) > (rand()%10000)/10000.0)
47             ans = tem;
48             now = next;
49         
50         T *= 0.98;
51     
52     printf("%.0f",ans);
53     return 0;
54 
View Code

 

最小球覆盖:poj2069

题目链接:https://vjudge.net/problem/POJ-2069

和上一题差不多,先以平均点做起始点,然后每次移动肯定是往距离当前点的最远点移动。

(这个没有接受更差解了)

技术图片
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #define eps 1e-6
 5 using namespace std;
 6 const int N = 40;
 7 int n;
 8 struct Point
 9     double x,y,z;
10 p[N],now;
11 double dis(Point u,Point v)
12     return sqrt( (u.x-v.x)*(u.x-v.x) + (u.y-v.y)*(u.y-v.y) + (u.z - v.z)*(u.z - v.z));
13 
14 int main()
15     while(~scanf("%d",&n) && n)
16         for(int i = 1 ; i <= n ;++i)
17             scanf("%lf %lf %lf",&p[i].x,&p[i].y,&p[i].z);
18             now.x+=p[i].x; now.y+=p[i].y; now.z += p[i].z;
19         
20         now.x/=n; now.y/=n; now.z/=n;
21         double T = 100;
22         double ans = 1e99;
23         while(T > eps)
24             int k = 1;
25             for(int i = 1;i <= n ;++i)
26                 if(dis(now,p[i]) > dis(now,p[k]) )
27                     k = i;
28                 
29             
30             double tem = dis(p[k],now);
31             ans = min(ans,tem);
32             now.x += ( (p[k].x - now.x)/tem )*T;
33             now.y += ( (p[k].y - now.y)/tem )*T;
34             now.z += ( (p[k].z - now.z)/tem )*T;
35             T *= 0.98;
36         
37         printf("%.6f\n",ans);
38     
39     return 0;
40 
View Code

 

至于最小园覆盖,模拟退火的话和最小求覆盖一样啦。不过最小圆覆盖有线性的点增量法(下一篇讲到),所以就不妨在这里了。

以上是关于模板模拟退火 费马点以及最小球覆盖的主要内容,如果未能解决你的问题,请参考以下文章

D.Country Meow 最小球覆盖 三分套三分套三分 && 模拟退火

poj 2420,模拟退火算法,费马点

Gym - 101981D Country Meow(模拟退火)

bzoj3680: 吊打XXX(模拟退火)

POJ2069 最小球覆盖 几何法和退火法

[POJ2420]A Star not a Tree?(模拟退火)