★【模拟退火】Texas Trip

Description
After a day trip with his friend Dick, Harry noticed a strange pattern of tiny holes in the door of his
SUV. The local American Tire store sells fiberglass patching material only in square sheets. What is the
smallest patch that Harry needs to fix his door?

Assume that the holes are points on the integer lattice in the plane. Your job is to find the area of the
smallest square that will cover all the holes.

Input
The first line of input contains a single integer T expressed in decimal with no leading zeroes, denoting
the number of test cases to follow. The subsequent lines of input describe the test cases.

Each test case begins with a single line, containing a single integer n expressed in decimal with no
leading zeroes, the number of points to follow; each of the following n lines contains two integers x
and y, both expressed in decimal with no leading zeroes, giving the coordinates of one of your points.

You are guaranteed that T ≤ 30 and that no data set contains more than 30 points. All points in each
data set will be no more than 500 units away from (0,0).

Output
Print, on a single line with two decimal places of precision, the area of the smallest square containing
all of your points.

Sample Input
2
4
-1 -1
1 -1
1 1
-1 1
4
10 1
10 -1
-10 1
-10 -1

Sample Output
4.00
242.00
找了半天,总算找到一个可读性好一点的模拟退火了,虽然好像和正统的模拟退火不太一样……
以下几段引自百度百科。
模拟退火算法是一种通用概率算法。
模拟退火算法可以分解为解空间、目标函数和初始解三部分。
模拟退火的基本思想:
(1) 初始化:初始温度T0(充分大),初始解状态S(是算法迭代的起点),每个T值的迭代次数L
(2) 对k=1,……,L做第(3)至第6步:
(3) 产生新解S'
(4) 计算增量Δt'=C(S')-C(S),其中C(S)为评价函数
                                                      Δt'
(5) 若Δt'<0则接受S′作为新的当前解,否则以概率exp(- ———)接受S'作为新的当前解.
                                                       T
(6) 如果满足终止条件则输出当前解作为最优解,结束程序。(终止条件通常取为连续若干个新解都没有被接受时终止算法。)
(7) T逐渐减少,且T->0,然后转第2步。


对于T的减少有如下几种方式:
            T0
1. T = —————— ;
        lg(1 + t)
          T0
2. T = ———— ;
        1 + t
3. T = T0 ·a^t .
4. ……

模拟退火算法的优化:
(1) 增加升温或重升温过程。在算法进程的适当时机,将温度适当提高,从而可激活各状态的接受概率,以调整搜索进程中的当前状态,避免算法在局部极小解处停滞不前。
(2) 增加记忆功能。为避免搜索过程中由于执行概率接受环节而遗失当前遇到的最优解,可通过增加存储环节,将“Best So Far”的状态记忆下来。
(3) 增加补充搜索过程。即在退火过程结束后,以搜索到的最优解为初始状态,再次执行模拟退火过程或局部性搜索。
(4) 对每一当前状态,采用多次搜索策略,以概率接受区域内的最优状态,而非标准SA的单次比较方式。
(5) 结合其他搜索机制的算法,如遗传算法、混沌搜索等。
(6)上述各方法的综合应用。

对于这道题,正统方法不知,可模拟退火水过。

题解来自神牛http://blog.csdn.net/jasonzhu8/article/details/5901337

枚举旋转角,逐步减小角度(每次让其正切值减半)从而逼近最优解。
对于每一个旋转角,设方向向量为(x, y),那么就可以借用点乘的几何意义将原点到每个点的有向线段全部投影到(x, y)上,再用类似的方法将与(x, y)垂直的向量(y, -x)也计算一遍,两者取较大,就可以将结果求出来。

标准的旋转变换:

程序中使用的旋转变换:


并且其中将向量的模长设为了100以减小相对误差。
Accode:

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
const int maxN = 40;
const double INF = 1e198, zero = 1e-12;
const int dx[] = {1, -1}, dy[] = {-1, 1};
int x[maxN], y[maxN], n, T;

double calc(double _x, double _y)
{
    double Min = INF, Max = -INF;
    for (int i = 0; i < n; ++i)
    {
        double tmp = _x * x[i] + _y * y[i];
        if (tmp > Max) Max = tmp;
        if (tmp < Min) Min = tmp;
    }
    double ans = Max - Min;
    Min = INF, Max = -INF;
    for (int i = 0; i < n; ++i)
    {
        double tmp = _y * x[i] - _x * y[i];
        if (tmp > Max) Max = tmp;
        if (tmp < Min) Min = tmp;
    }
    (Max - Min > ans) ? (ans = Max - Min) : ans;
    return ans * ans / (_x * _x + _y * _y);
//记得要除以模长,防止开方,直接返回求得正方形的面积。
}

double SAA()
{
    double sx = 100, sy = 0, T = 1, ans = calc(sx, sy);
    for (; T > zero; T /= 2)
    while (1)
    {
        bool ok = 0;
        for (int i = 0; i < 2; ++i)
        {
            double tx = sx + dx[i] * sy * T,
                   ty = sy + dy[i] * sx * T,
                   Now = calc(tx, ty),
                   tmp = sqrt(1e4 / (tx * tx + ty * ty));
            tx *= tmp, ty *= tmp;
	//保持模长不变。
	    if (Now < ans)
            {ans = Now; ok = 1; sx = tx, sy = ty;}
	//这里可以只接受更优的解,因为当前决策
	//并不影响下一步是否能达到最优解。
        } //向顺时针和逆时针两个方向都要旋转一遍。
        if (!ok) break;
    }
    return ans;
}

int main()
{
    freopen("Texas_Trip.in", "r", stdin);
    freopen("Texas_Trip.out", "w", stdout);
    for (scanf("%d", &T); T; --T)
    {
        scanf("%d", &n);
        for (int i = 0; i < n; ++i)
            scanf("%d%d", x + i, y + i);
        printf("%.2lf\n", SAA());
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值