模拟退火((SA))和随机化贪心
物理上的模拟退火
固体退火原理,将固体加温至充分高,再让其徐徐冷却,加温时,固体内部粒子随温升变为无序状,内能增大,而徐徐冷却时粒子渐趋有序,在每个温度都达到平衡态,最后在常温时达到基态,内能减为最小。
启发
根据物理上的模拟退火,我们可以吸取一些经验,采用模拟退火的方法。
模拟退火是一个非常优秀的骗分甚至可以秒杀正解的算法,其能够很好地通过每次时间复杂度很小的贪心,然后不停贪心,就算这样那贪心许多次之后时间复杂度依然很小,甚至可以秒杀正解,而且模拟退火是有变化幅度的,而算法性能和其结果与初始值有关是他的特点,既是它的优点也是它的缺点。优点在于你可以控制它的复杂度,但缺点也就是该算法容易被卡。所以随机化贪心要少用啊。不然人品就败光了。
过程
-
初始化:初始温度(T)(充分大),初始解状态(S)(是算法迭代的起点)
-
产生新解(S′)
-
计算增量(Δt′=C(S′)-C(S)),其中(C(S))为评价函数
-
若(Δt′<0)则接受(S′)作为新的当前解,否则以概率(exp(-Δt′/T))接受(S′)作为新的当前解.
-
如果满足终止条件或温度过低则输出当前解作为最优解,结束程序。终止条件通常取为连续若干个新解都没有被接受时终止算法。
-
(T)逐渐减少,且(T>=0),然后转第(2)步。
思想
其思想主要结合了退火和信息学的特点,采取模拟的算法来实现随机化贪心,有人这么比喻,
(Code)([JSOI2004]平衡点)
#include <bits/stdc++.h>
#pragma GCC optimize(2)
#pragma GCC optimize(3)
const double eps = 1e-253;
const double t = 0.99389;
int n; double xans, yans, xx, yy, ans = 1e18 + 7, now;
using namespace std;
struct de {
int x, y, w;
}data[10010];
double jisuan(double x, double y)
{
double sum = 0;
for (int i = 1; i <= n; i++)
{
double dx = x - data[i].x, dy = y - data[i].y;
sum += data[i].w * (sqrt(dx * dx + dy * dy));
}
return sum;
}
void tuihuo(double T)
{
while (T > eps)
{
double xx = xans + (rand() * 2 - RAND_MAX) * T;
double yy = yans + (rand() * 2 - RAND_MAX) * T;
double now = jisuan(xx, yy);
double del = now - ans;
if (del < 0)//如果now比ans要优
{
xans = xx;
yans = yy;
ans = now;
}
else if(exp(-del / T) * RAND_MAX>rand())//能否接受这个差
{
xans = xx;
yans = yy;
}
T *= t;
}
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d%d%d", &data[i].x, &data[i].y, &data[i].w);
tuihuo(1926), tuihuo(1926), tuihuo(1926);
printf("%.3lf %.3lf", xans, yans);
return 0;
}