模拟退火算法来源于固体退火原理,更多的化学物理公式等等这里不再废话,我们直接这么来看
模拟退火算法简而言之就是一种暴力搜索算法,用来在一定概率下查找全局最优解
找的过程和固体退火原理有所联系,一般来讲,就是设置应该初始温度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
大致就是这样吧?