社区讨论

模拟退火,但是不知道是思路正确性的问题还是参数需要微调

P5544[JSOI2016] 炸弹攻击1参与者 2已保存回复 9

讨论操作

快速查看讨论及其快照的属性,并进行相关操作。

当前回复
9 条
当前快照
1 份
快照标识符
@lo37bbua
此快照首次捕获于
2023/10/24 01:58
2 年前
此快照最后确认于
2023/10/24 01:58
2 年前
查看原帖
思路是:为避免每次模拟退火随机到的点偏离敌人太远,所以每次执行模拟退火随机的第一个点就直接随机敌人
CPP
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <ctime>
#include <cctype>

using namespace std;

#define x first
#define y second

struct Build {double x, y, r;};
struct Enemy {double x, y;};

typedef pair<double, double> PDD;
const int N = 20, M = 1010;
Build buil[N];
Enemy enem[M];
int n, m, R;
int ans;

inline int read()
{
    int s = 0, w = 1;
    char ch = getchar();
    while (!isdigit(ch)) { if (ch == '-') w = -1; ch = getchar();}
    while (isdigit(ch)) s = (s << 3) + (s << 1) + ch - '0', ch = getchar();
    return s * w;
}

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

int calc(PDD p)
{
    double t = R;
    for (int i = 1; i <= n; ++i)
    {
        double dx = buil[i].x - p.x;
        double dy = buil[i].y - p.y;
        double dist = sqrt(dx * dx + dy * dy);
        t = min(t, dist - buil[i].r);
    }
    
    int cnt = 0;
    for (int i = 1; i <= m; ++i)
    {
        double dx = enem[i].x - p.x;
        double dy = enem[i].y - p.y;
        double dist = sqrt(dx * dx + dy * dy);
        if (dist <= t) ++ cnt;
    }
    ans = max(ans, cnt);
    return cnt;
}

void simulate_anneal()
{
    int k = rand() % m + 1;
    PDD cur(enem[k].x, enem[k].y);
    for (double t = 2500; t > 1e-2 && 
    (double)clock() / CLOCKS_PER_SEC <= 0.95; t *= 0.99)
    {
        PDD np(rand(cur.x - t, cur.x + t), rand(cur.y - t, cur.y + t));
        int x = calc(cur), y = calc(np);
        int d = y - x;
        if (exp(d / t) > rand(0, 1)) cur = np;
    }
}

int main()
{
    n = read(), m = read(), R = read();
    for (int i = 1; i <= n; ++i)
        buil[i].x = read(), buil[i].y = read(), buil[i].r = read();
    for (int i = 1; i <= m; ++i)
        enem[i].x = read(), enem[i].y = read();
    
    while ((double)clock() / CLOCKS_PER_SEC <= 0.95) simulate_anneal();
    
    printf("%d", ans);
    return 0;
}
这题因为卡时评测时间太长,怕占用评测资源就到了别的人比较少的OJ去交了十几页
但是把这句话改一下:
CPP
if (exp(d / t) > rand(0, 1)) cur = np;
改成:
CPP
if (d > 0) cur = np;
(其实就是把模拟退火变成爬山法)
这么说模拟退火的做法是思路没问题,但是参数调的不对吗?
求问dalao,感谢!

回复

9 条回复,欢迎继续交流。

正在加载回复...