一些乱搞的退火题
退火(全称叫模拟退火)是一种随机乱搞的玄学算法,大概是珂学家们发现随机贪心往往效果不好,所以出现了在原策略上进行改动的方法。后来发现单纯的小改动容易陷入局部最优解,就又有了这个玄学的退火。
不过我并不想讲这个具体的过程,只是简单说一些细节:
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;
}