初探 模拟退火算法 POJ2420 HDU1109

本文深入探讨了模拟退火算法的原理与应用,通过两个具体问题的求解过程,展示了如何利用该算法寻找全局最优解,同时介绍了算法的参数设置与实现细节。

模拟退火算法来源于固体退火原理,更多的化学物理公式等等这里不再废话,我们直接这么来看

模拟退火算法简而言之就是一种暴力搜索算法,用来在一定概率下查找全局最优解

找的过程和固体退火原理有所联系,一般来讲,就是设置应该初始温度T(通常为100),一个退火的系数K(通常为0.98),一个退火的结束温度T1(通常为1e-8)

每进行一次查找,T = T * K

如果T = T1,查找停止,(即固体退火完成)

这里以POJ2420和HDU1109为例

POJ2420题目地址:

http://poj.org/problem?id=2420

这个题的题意是给n个点,让你找一个点的距离到这n个点的距离最小

直接设好温度和方向搜索就好了

注意POJ的G++编译器输出%0.0lf会WA

 1 //POJ2420
 2 #include <iostream>
 3 #include <cstdio>
 4 #include <cstring>
 5 #include <string>
 6 #include <map>
 7 #include <set>
 8 #include <list>
 9 #include <deque>
10 #include <queue>
11 #include <stack>
12 #include <vector>
13 #include <cmath>
14 #include <algorithm>
15 using namespace std;
16 #define it iterator
17 #define ll long long
18 #define eb emplace_back
19 #define lowbit(x) x & -x
20 #define all(x) x.begin(),x.end()
21 #define ZERO(a) memset(a,0,sizeof(a))
22 #define MINUS(a) memset(a,0xff,sizeof(a))
23 #define per(x,a,b) for(int x = a; x <= b; x++)
24 #define rep(x,a,b) for(int x = a; x >= b; x--)
25 #define IO ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
26 
27 const int birth = 19260817;
28 const int mo = 998244353;
29 const int maxn = 1e5 + 10;
30 const int mod = 1e9 + 7;
31 const int INF = 1e9;
32 const double eps = 1e-8;//停止的温度
33 
34 char mp[2010][2010];
35 bool vis[2010][2010];
36 //******************THE PROGRAM BEGINING******************
37 int n;
38 int d[4][2] = { { 1,0 },{ -1,0 },{ 0,1 },{ 0,-1 } };
39 
40 struct node
41 {
42     double x, y;
43     node() {}
44 }p[110];
45 
46 double dis(node p[], node x, int n)//求点X与各点距离
47 {
48     double sum = 0;
49     per(i, 0, n - 1)
50         sum += sqrt(pow(p[i].x - x.x, 2) + pow(p[i].y - x.y, 2));
51 
52     return sum;
53 }
54 
55 double solve(node p[], int n)
56 {
57     double ans = 0x3f3f3f3f;
58     double T = 100;//初始温度
59     double e = 0.98;//系数
60     node now = p[0];//设置初始点(随便设个就好了)
61     while (T > eps)
62     {
63         bool flag = 1;
64         while (flag)
65         {
66             flag = 0;
67             per(i, 0, 3)
68             {
69                 node next = now;
70                 next.x += d[i][0];
71                 next.y += d[i][1];
72                 double temp = dis(p, next, n);
73                 if (ans > temp)//更新
74                 {
75                     ans = temp;
76                     flag = 1;
77                     now = next;
78                 }
79             }
80         }
81         T *= e;//冷却
82     }
83 
84     return ans;
85 }
86 
87 int main()
88 {
89     //IO;
90     scanf("%d", &n);
91     per(i, 0, n - 1)
92         scanf("%lf %lf", &p[i].x, &p[i].y);
93     printf("%.0f\n", solve(p,n));
94     return 0;
95 }

HDU1109题目地址:

http://acm.hdu.edu.cn/showproblem.php?pid=1109

这个题的题意是给一个大小为x*y的房间和n个点,让你找在这个房间里一个点,这个点到n个点中离得它最近点之间距离是最大的

这个题如果跟上题一样做的话,有可能会掉进局部最优解,有两种解决方法:

1:在更新数值的时候允许一定几率跳出当前解法,几率随温度降低而降低

2:多设置几个点同时查找

我这里选择了第二种做法,个人认为更靠谱一点(其实都是随机的了,不过我觉得随机的越少越稳定)

这个题还要注意精度问题,这里采用2种不同的移动精度来保证速度和精度,根据当前温度来决定精度优先或移动优先

本来还打算记忆化搜索来加快速度的,结果MLE了。。。。

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <string>
  5 #include <map>
  6 #include <set>
  7 #include <list>
  8 #include <deque>
  9 #include <queue>
 10 #include <stack>
 11 #include <vector>
 12 #include <cmath>
 13 #include <algorithm>
 14 using namespace std;
 15 #define it iterator
 16 #define ll long long
 17 #define eb emplace_back
 18 #define lowbit(x) x & -x
 19 #define all(x) x.begin(),x.end()
 20 #define ZERO(a) memset(a,0,sizeof(a))
 21 #define MINUS(a) memset(a,0xff,sizeof(a))
 22 #define per(x,a,b) for(int x = a; x <= b; x++)
 23 #define rep(x,a,b) for(int x = a; x >= b; x--)
 24 #define IO ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
 25 
 26 const int birth = 19260817;
 27 const int mo = 998244353;
 28 const int maxn = 1e5 + 10;
 29 const int mod = 1e9 + 7;
 30 const int INF = 0x3fffffff;
 31 const double eps = 1e-8;
 32 
 33 //******************THE PROGRAM BEGINING******************
 34 int n,x,y;
 35 int d[8][2] = { { 1,0 },{ -1,0 },{ 0,1 },{ 0,-1 },{ 1,1 },{ -1,1 },{ 1,1 },{ -1,-1 } };
 36 double d1[8][2] = { { 0.001,0 },{ -0.001,0 },{ 0,0.001 },{ 0,-0.001 },{ 0.001,0.001 },{ -0.001,0.001 },{ 0.001,-0.001 },{ -0.001,-0.001 } };
 37 double ans[30];
 38 //bool vis[10000][10000];
 39 struct node
 40 {
 41     double x, y;
 42     node() {}
 43 }p[1010], r[6];;
 44 
 45 double dis(node p[], node x, int n)
 46 {
 47     double MIN = INF;
 48     per(i, 0, n - 1)
 49     {
 50         MIN = min(MIN,sqrt(pow(p[i].x - x.x, 2.0) + pow(p[i].y - x.y, 2.0)));
 51     }
 52 
 53     return MIN;
 54 }
 55 
 56 node solve(node p[], int n)
 57 {
 58     double T = 100;
 59     double e = 0.98;
 60     node now;
 61     srand(birth);
 62     per(i, 0, 5)
 63     {
 64         r[i].x = rand() % x;
 65         r[i].y = rand() % y;
 66         ans[i] = dis(p, r[i], n);
 67         //vis[(int)r[i].x][(int)r[i].y] = 1;
 68     }
 69     while (T > eps)
 70     {
 71         bool flag = 1;
 72         while (flag)
 73         {
 74             flag = 0;
 75             per(j, 0, 5)
 76                 per(i, 0, 8)
 77                 {
 78                     node next = r[j];
 79                     //if (T < 0.1)
 80                         //printf("1: %lf %lf\n", next.x, next.y);
 81                     if (T > 0.5)
 82                     {
 83                         next.x += d[i][0];
 84                         next.y += d[i][1];
 85                     }                        
 86                     else
 87                     {
 88                         next.x += d1[i][0];
 89                         next.y += d1[i][1];
 90                     }
 91                     if (next.x < 0 || next.y < 0 || next.x > x || next.y > y)
 92                         continue;
 93                     //if (T > 1 && vis[(int)next.x][(int)next.y])
 94                         //continue;
 95                     double temp = dis(p, next, n);
 96                     //printf("2: %lf %lf\n", next.x ,next.y);
 97                     if (ans[j] < temp)
 98                     {
 99                         ans[j] = temp;
100                         flag = 1;
101                         r[j] = next;
102                         //if (T > 1)
103                             //vis[(int)next.x][(int)next.y] = 1;
104                     }
105                 }
106         }
107         T *= e;
108     }
109     double min = 0;
110     int pos;
111     per(i, 0, 5)
112     {
113         if (ans[i] > min)
114             min = ans[i],pos = i;
115     }
116     return r[pos];
117 }
118 
119 int main()
120 {
121     //IO;
122     int t;
123     scanf("%d", &t);
124     while (t--)
125     {
126         //ZERO(vis);
127         scanf("%d %d %d", &x, &y, &n);
128         per(i, 0, n - 1)
129             scanf("%lf %lf", &p[i].x, &p[i].y);
130         node ans = solve(p, n);
131         printf("The safest point is (%.1f, %.1f).\n", ans.x, ans.y);
132         /*
133         node a;
134         a.x = 1433.0;
135         a.y = 1669.9;
136         cout << dis(p, a, n) << endl;
137         a.y = 1669.8;
138         cout << dis(p, a, n) << endl;
139         */
140     }
141     return 0;
142 }

实际使用中,我们也不要把思路局限在对随机点的上下左右移动上,更要根据题目来判断对时间上和方向上的把握,灵活改变策略

如:POJ2069

大致就是这样吧?

转载于:https://www.cnblogs.com/qq965921539/p/9794300.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值