一些乱搞的退火题

退火(全称叫模拟退火)是一种随机乱搞的玄学算法,大概是珂学家们发现随机贪心往往效果不好,所以出现了在原策略上进行改动的方法。后来发现单纯的小改动容易陷入局部最优解,就又有了这个玄学的退火。

不过我并不想讲这个具体的过程,只是简单说一些细节:
1、记住\(e^{-ans / t}\)是接受解的概率(\(ans\)指当前解与最优解差了多少),很良心的,我给出这个函数的一些结果作为参考:
\(e^{-0.001}=0.9990005\)
\(e^{-0.01}=0.99005\)
\(e^{-0.1}=0.905\)
\(e^{-1}=0.36788\)
\(e^{-10}=0.0000454\)
\(e^{-100}=0.0000000000000000000000000000000000000000000372\)
所以你的退火也是有脑子的!

2、温度既可以决定接受更差解的概率,也可以决定在当前策略上调整的步幅,当然啦,温度越高步幅就越大了。

3、调整决策的具体方法和数值比较随意,为了方便,如果决策由一个序列决定,我们可以随机交换两个值来得到新决策(这个时候步幅跟温度无关)。如果是平面或空间中实数域上的一个点,我们可以向随机一个方向移动与温度呈正相关的距离。如果是整数域,我们可以把温度四舍五入当做步幅。

4、\(srand(19260817)\)之后再\(srand(rand())\)两次有奇效,初温末温和\(\Delta\)根据题目来定,据说在得到解的优化率比较平均时能得到较好的结果。

5、如果是传统题,可以加入卡时操作。尽管你有可能会得到洛谷rank-1,但是你稳呀

提供个板子(伪代码):

inline void SA()
{
    double t = t0;
    while (t > t1) {
    	get_a_new_solution(t);
        double ans = calc(now) - calc(pre);
        if (ans < 0) pre = now, best_answer = calc(now);
        else if (exp(-ans / t) * RAND_MAX > rand()) pre = now;
        t *= delta;
    }
}

看起来十分\(eazy\)

然后是一些水题:
P1337 [JSOI2004]平衡点 / 吊打XXX
XXX是有很多妹子的神仙GTY
把上面板子改改就好了
P2503 [HAOI2006]均分数据
每次随出一个新序列,用朴素的三方\(dp\)去判断解的优越性
P4360 [CEOI2004]锯木厂选址
每次随出两个点来,代回去\(O(1)\)判断

顺便补一下第三题的斜率优化做法:

#include <iostream>
#include <cstring>
#include <cstdio>

const int maxn = 2e4 + 7;

using namespace std;

int n, ans = 2e9;
int s[maxn], d[maxn];
int v[maxn];
int f[maxn];
int q[maxn], l, r;

inline int read()
{
    int X = 0; char ch = getchar();
    while (ch < '0' || ch > '9') ch = getchar();
    while (ch >= '0' && ch <= '9') X = X * 10 + ch - '0', ch = getchar();
    return X; 
}

inline double k(int i, int j)
{
    return 1.0 * (d[j] * s[j] - d[i] * s[i]) / (s[j] - s[i]);
}

int main(void)
{
    cin >> n;
    for (int i = 1; i <= n; i++) s[i] = s[i - 1] + read(), d[i] = read();
    for (int i = 1; i <= n + 1; i++) v[i] = v[i - 1] + s[i - 1] * d[i - 1];
    for (int i = n; i; i--) d[i] += d[i + 1];
    q[++r] = 1;
    for (int i = 2; i <= n; i++) {
        while (l < r && (d[q[l]] - d[i]) * s[q[l]] < (d[q[l + 1]] - d[i]) * s[q[l + 1]]) l++;
        f[i] = v[n + 1] - d[i] * s[i] - (d[q[l]] - d[i]) * s[q[l]];
        while (r > l + 1 && k(i, q[r]) > k(i, q[r - 1])) r--;
        q[++r] = i;
    }
    for (int i = 2; i <= n; i++) ans = min(ans, f[i]);
    cout << ans << endl;
    
    return 0;
}
posted @ 2019-03-08 19:56  star_city  阅读(128)  评论(0编辑  收藏  举报