乱搞算法

爬山算法

  对于单峰函数可行

  实现:

  先随机一个答案,之后每次将其与与其在一定范围内相邻的答案选举一个最优解,每一次运算后范围逐渐缩小,直到范围小于阈值

  例题:[JSOI2008]球形空间产生器

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;

const int N = 20;

int n;
double dis[N], cans[N], f[N][N], ans[N];

inline void solve()
{
    double res = 0;
    for (int i = 1; i <= n + 1; i++)
    {
        dis[i] = 0;
        cans[i] = 0;
        for (int j = 1; j <= n; j++) dis[i] += pow(f[i][j] - ans[j], 2);
        dis[i] = sqrt(dis[i]);
        res += dis[i];
    }

    res /= n + 1;
    for (int i = 1; i <= n + 1; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            cans[j] += (dis[i] - res) * (f[i][j] - ans[j]) / res;
        }
    }
}

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n + 1; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            scanf("%lf", &f[i][j]);
            ans[j] += f[i][j];
        }
    }

    for (int i = 1; i <= n; i++) ans[i] /= (n + 1);
    for (double t = 10001; t >= 0.0001; t *= 0.99995)//每次缩小范围(降温)
    {
        solve();
        for (int i = 1; i <= n; i++) ans[i] += cans[i] * t;
    }
    for (int i = 1; i <= n; i++) printf("%.3lf ", ans[i]);
}
View Code

模拟退火

  任何函数可行

  实现:

  先随机一个答案,之后每次在范围内随机一个解,若此解更优则更新,否则以一定概率接受该解

  例题:[AcWing3167]星星还是树

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;

#define x first
#define y second

typedef pair<double, double> PDD;

const int N = 110;

int n;
double ans;
PDD a[N];

inline double rand(double l, double r)
{
    return (double)rand() / RAND_MAX * (r - l) + l;
}

inline double get_dis(PDD a, PDD b)
{
    return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2));
}

inline double calc(PDD X)
{
    double res = 0;
    for (int i = 1; i <= n; i++) res += get_dis(X, a[i]);
    ans = min(ans, res);
    return res;
}

inline void solve()
{
    PDD cur(rand(0, 10000), rand(0, 10000));
    for (double t = 1e4; t > 1e-4; t *= 0.9)//降温,每次缩小范围
    {
        PDD nw(rand(cur.x - t, cur.x + t), rand(cur.y - t, cur.y + t));
        double res = calc(nw) - calc(cur);
        if (exp(-res / t) > rand(0, 1)) cur = nw;//若当前解更优则更新,否则以一定概率接受答案
    }
}

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%lf%lf", &a[i].x, &a[i].y);

    ans = 1e8;
    for (int i = 1; i <= 100; i++) solve();//多退火几次,答案更准确
    printf("%.0lf", ans);
}
View Code

 

posted on 2021-05-12 21:00  ArrogHie  阅读(107)  评论(0编辑  收藏  举报