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

最近学了一波模拟退火。个人感觉,就是随机算法,然后我们的目标点,一开始温度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

 

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

 

简单多边形塞入最大的圆:hdu3644

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3644

真的是模拟退火的奇淫怪招。这题区别就是,之前的模拟退火都是一个点在跳来跳去,这一题是多个点(初始化为所有边的中点)跳来跳去,并且各自记录这多个点之前跳到最优解的状态,并且一个T每个点跳多次。

  1 /*************************************************************************
  2   > File Name: hdu3644.cpp
  3 # File Name: hdu3644.cpp
  4 # Author : xiaobuxie
  5 # QQ : 760427180
  6 # Email:760427180@qq.com
  7 # Created Time: 2019年10月31日 星期四 15时57分41秒
  8  ************************************************************************/
  9 
 10 #include<bits/stdc++.h>
 11 #include<iostream>
 12 #include<cstdio>
 13 #include<map>
 14 #include<cmath>
 15 #include<cstring>
 16 #include<set>
 17 #include<queue>
 18 #include<vector>
 19 #include<algorithm>
 20 using namespace std;
 21 typedef long long ll;
 22 #define inf 0x3f3f3f3f
 23 #define eps 1e-5
 24 const double pi = acos(-1.0);
 25 int sgn(double x){
 26     if(fabs(x) < eps) return 0;
 27     if(x<0) return -1;
 28     return 1;
 29 }
 30 const int N = 55;
 31 struct Point{
 32     double x,y,r;
 33     Point(){x=y=0;}
 34     Point(double a,double b){x=a,y=b;}
 35     Point operator - (const Point& b)const
 36     {return Point(x-b.x,y-b.y);}
 37     Point operator + (const Point& b)const
 38     {return Point(x+b.x,y+b.y);}
 39     Point operator * (const double& b)const
 40     {return Point(x*b,y*b);}
 41     double dot (const Point& b)const
 42     {return x*b.x+y*b.y;}
 43     double cross (const Point& b,const Point& c)const
 44     {return (b.x - x)*(c.y - y) - (c.x - x)*(b.y - y);}
 45     double dis (const Point& b)const
 46     {return sqrt( (x-b.x)*(x-b.x) + (y-b.y)*(y-b.y) );}
 47     bool OnLine (const Point& st,Point& ed)const
 48     {return !sgn(cross(st,ed));}
 49     bool OnSeg (const Point& st,Point& ed)const
 50     {return OnLine(st,ed) && (*this - ed).dot(*this - st) < eps;}
 51 }p[N],tp[N];
 52 struct Segment{
 53     Point s,e;
 54 };
 55 int SegCross(Segment a,Segment b){
 56     double x1 = a.s.cross(a.e,b.s);
 57     double x2 = a.s.cross(a.e,b.e);
 58     double x3 = b.s.cross(b.e,a.s);
 59     double x4 = b.s.cross(b.e,a.e);
 60     if( b.e.OnLine(a.s,a.e) && b.s.OnSeg(a.s,a.e) ||
 61             b.s.OnLine(a.s,a.e) && b.e.OnSeg(a.s,a.e) ||
 62             a.e.OnLine(b.s,b.e) && a.s.OnSeg(b.s,b.e) ||
 63             a.s.OnLine(b.s,b.e) && a.e.OnSeg(b.s,b.e) )
 64         return 2;
 65     else if( sgn(x1*x2) <= 0 && sgn(x3*x4) <= 0) return 1;
 66     return 0;
 67 }
 68 int PointIn(Point p0,Point p[],int n){
 69     Segment L,S;
 70     Point temh,teml;
 71     L.s = p0; L.e = Point(inf,p0.y);
 72     int cnt = 0;
 73     for(int i = 1;i<=n;++i){
 74         S.s = p[i-1]; S.e = p[i];
 75         if(p0.OnSeg(S.s,S.e)) return 0;
 76         if(S.s.y == S.e.y) continue;
 77         if(S.s.y > S.e.y) temh = S.s,teml = S.e;
 78         else temh = S.e,teml = S.s;
 79         if(temh.OnSeg(L.s,L.e)) cnt++;
 80         else if(SegCross(L,S) == 1 && teml.OnSeg(L.s,L.e) == 0) ++cnt;
 81     }
 82     if(cnt%2) return -1;
 83     return 1;
 84 } 
 85 double seg_point_dis(Point s,Point e,Point o){
 86     double a,b,c;
 87     a = o.dis(s);
 88     b = o.dis(e);
 89     c = s.dis(e);
 90     if(sgn(a*a + c*c - b*b) < 0) return a;
 91     if(sgn(b*b + c*c - a*a) < 0) return b;
 92     return fabs( o.cross(s,e)/c );
 93 
 94 }
 95 double cal(Point p0,Point p[],int n){
 96     double res = inf;
 97     for(int i = 0;i<n;++i){
 98         double d = seg_point_dis(p[i],p[i+1],p0);
 99         res = min(res,d);
100     }
101     return res;
102 }
103 int main(){
104     srand(time(0));
105     int n;
106     double r;
107     while(~scanf("%d",&n) && n){
108         Point now = (Point){0,0};
109         double mxx = 0,mix = inf ,mxy = 0, miy = inf;
110         for(int i = 0;i<n;++i){
111             scanf("%lf %lf",&p[i].x,&p[i].y);
112             now.x += p[i].x;
113             now.y += p[i].y;
114             mxx = max(mxx,now.x); mix = min(mix,now.x);
115             mxy = max(mxy,now.y); miy = min(miy,now.y);
116         }
117         p[n] = p[0];
118         for(int i = 0;i<n;++i){
119             tp[i].x = (p[i].x + p[i+1].x)/2;
120             tp[i].y = (p[i].y + p[i+1].y)/2;
121             tp[i].r = 0;
122         }
123         mxx -= mix; mxy -= miy;
124         scanf("%lf",&r);
125         double T = sqrt(mxx*mxx + mxy*mxy)/2;
126         bool ok = 0;
127         while(T > eps && (!ok) ){
128             for(int i = 0;i<n && (!ok) ;++i){
129                 for(int u = 1;u<=30 && (!ok);++u){
130                     double ang = (rand()%1000)/1000.0 * 2*pi;
131                     Point tem = Point(tp[i].x + T*cos(ang),tp[i].y + T*sin(ang));
132                     if(PointIn(tem,p,n) == -1 ){
133                         tem.r = cal(tem,p,n);
134                         if(tem.r > tp[i].r ) tp[i] = tem;
135                         if(sgn(tp[i].r - r) >= 0){
136                             ok = 1;
137                         }
138                     }
139                 }
140             }
141             T*=0.6;
142         }
143         if(ok) puts("Yes");
144         else puts("No");
145     }
146     return 0;
147 }
View Code

所以模拟退火的姿势还是很重要的。。。

posted @ 2019-10-03 13:42  小布鞋  阅读(303)  评论(0编辑  收藏  举报