模拟退火算法的理论讲解:
《模拟退火与遗传算法》
《模拟退火算法》
POJ: 2069 Super star
求一个半径最小的球体,包含所有的点。模拟退火,不断缩小半径,搜索……
#include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <cstdlib> using namespace std; #define N 33 #define eps 1e-7 struct POINT { double x, y, z; } p[N], s; int n; inline double dist(const POINT &a, const POINT &b) { return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z)); } void solve() { s.x = s.y = s.z = 0; double ans = 1e20, delta = 100; while (delta > eps) { int d = 0; for (int i=1; i<n; i++) if (dist(s, p[i]) > dist(s, p[d])) d = i; double md = dist(s, p[d]); if (ans > md) ans = md; s.x += (p[d].x-s.x)/md*delta; s.y += (p[d].y-s.y)/md*delta; s.z += (p[d].z-s.z)/md*delta; delta *= 0.98; } printf("%.5lf\n", ans); } int main() { while (scanf("%d", &n) == 1 && n) { for (int i=0; i<n; i++) scanf("%lf%lf%lf", &p[i].x, &p[i].y, &p[i].z); solve(); } return 0; }
POJ:1379 Run Away
基础模拟退火题目。
在平面内部随机取NUM个点,然后对每一个点进行“退火”(随机向各个方向移动T次,寻求最优结果,不断增大圆的半径)。
从NUM个结果中找到半径最大的点即可。
附上一篇国家集训队论文:浅谈随机化思想在几何问题中的应用
#include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <string> #include <cstdlib> using namespace std; #define N 1005 #define NUM 30 #define eps 1e-2 struct POINT { double x, y, d; POINT() {} POINT(double _x, double _y): x(_x), y(_y) {} } s, p[N], rp[NUM]; int x, y, n; double dis(POINT a) { double ret = 1e20; double tmp; for (int i=0; i<n; i++) { tmp = sqrt((a.x-p[i].x)*(a.x-p[i].x) + (a.y-p[i].y)*(a.y-p[i].y)); if (tmp < ret) ret = tmp; } return ret; } void Init() { scanf("%d%d%d", &x, &y, &n); for (int i=0; i<n; i++) scanf("%lf %lf", &p[i].x, &p[i].y); double d; for (int i=0; i<NUM; i++) { rp[i].x = rand()%x + 1; rp[i].y = rand()%y + 1; rp[i].d = dis(rp[i]); } } void solve() { double delta = (double)max(x, y)/sqrt((double)n)+1; double theta; POINT t; while (delta > eps) { for (int i=0; i<NUM; i++) { for (int k=0; k<NUM; k++) { theta = rand(); t.x = rp[i].x + cos(theta)*delta; t.y = rp[i].y + sin(theta)*delta; t.d = dis(t); if (0<=t.x && t.x<=x && 0<=t.y && t.y<=y) { if (t.d > rp[i].d) rp[i] = t; } } } delta *= 0.8; } int k = 0; for (int i=1; i<NUM; i++) if (rp[k].d < rp[i].d) k = i; printf("The safest point is (%.1lf, %.1lf).\n", rp[k].x, rp[k].y); } int main() { int cas; scanf("%d", &cas); while (cas--) { Init(); solve(); } return 0; }
POJ:2420 A Star not a Tree
求费马点:到所有点距离和最短的点称之为费马点。然后输出最短距离和。简单的模拟退火。
上代码:里面向不同方向走的时候,才用了随机化的方法,我取了10这个常数,这个理论上没有要求必须上下左右四个方向或者八个方向什么的,满足随机即可,这个常数的选择应该是比较凑巧可以过掉题目的。Just so so 了哈。。。
#include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <algorithm> using namespace std; #define N 110 #define eps 1e-2 struct POINT { double x, y; } p[N], s, c; int n; double dist(POINT &a) { double ret = 0; for (int i=0; i<n; i++) ret += sqrt((p[i].x-a.x)*(p[i].x-a.x)+(p[i].y-a.y)*(p[i].y-a.y)); return ret; } void solve() { s.x = s.y = 0; for (double delta = 10000.0; delta>eps; delta*=0.9) { for (int i=0; i<10; i++) { double t = rand(); c.x = s.x + cos(t)*delta; c.y = s.y + sin(t)*delta; if (dist(c) < dist(s)) s = c; } } printf("%.0lf\n", dist(s)); } int main() { scanf("%d", &n); for (int i=0; i<n; i++) scanf("%lf%lf", &p[i].x, &p[i].y); solve(); return 0; }