洛谷 P1337 [JSOI2004]平衡点 / 吊打XXX

在这里插入图片描述
在这里插入图片描述
具体题目见洛谷 P1337 [JSOI2004]平衡点 / 吊打XXX

方法一:模拟退火

思路:当绳子平衡时,系统的能量最小,则此时物体总的重力势能要最小,也就是物体重量一定的情况下绳长最长,即桌子上的绳长最短。使用模拟退火算法,设定适当的系数,产生新解时生成[-t*RAND_MAX,t*RAND_MAX)的随机变动范围(rand():[0,RAND_MAX))

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1005;
const int T0 = 3000;//起始温度,要足够大
const double Tk = 1e-15;//终止温度,要足够小
const double delta = 0.996;//降温系数,要接近1
int n;
struct node { int x, y, w; }obj[maxn];
double ansX, ansY, ansEnergy;//最终答案 
double energy(double x, double y) {//总能量越小越稳定 
    double energy = 0;
    for (int i = 1; i <= n; i++) {
        double dx = x - obj[i].x;
        double dy = y - obj[i].y;
        energy += sqrt(dx * dx + dy * dy) * obj[i].w;
    }
    return energy;
}
void SA(){//模拟退火 
    double t = T0;//温度要足够高 
    while (t > Tk) {//略大于0
        //随机产生新解,生成[-t*RAND_MAX,t*RAND_MAX)的随机变动范围(rand():[0,RAND_MAX))
        double newX = ansX + (rand() * 2 - RAND_MAX) * t;
        double newY = ansY + (rand() * 2 - RAND_MAX) * t;
        double newEnergy = energy(newX, newY);
        double deltaE = newEnergy - ansEnergy;
        if (deltaE < 0)//newEnergy<ansEnergy 如果此答案更优则接受 
            ansX = newX, ansY = newY, ansEnergy = newEnergy;
        else if (exp(-deltaE / t) * RAND_MAX > rand())//否则根据多项式概率接受
            ansX = newX, ansY = newY;//不更新ansEnergy
        t *= delta;//降温
    }
}
void solve() {
    ansX /= n, ansY /= n;//以平均数作为初始答案
    ansEnergy = energy(ansX, ansY);
    SA();//SA();SA();可以多跑几遍退火,增加得到最优解的概率
}
int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> obj[i].x >> obj[i].y >> obj[i].w;
        ansX += obj[i].x; ansY += obj[i].y;
    }
    solve();
    printf("%.3lf %.3lf\n", ansX, ansY);
    return 0;
}
关于调参:
  1. 如果答案不是最优:
    ①增大 Δ Δ Δ
    ②调整 T 0 , T k T_0,T_k T0,Tk T 0 , T k T_0,T_k T0,Tk 需要根据值域对应调整,保证每次随机出的变化幅度不会跑太远
    ③多跑几遍 SA()
    ④尝试更换随机数种子,或者 srand(rand())。总之,总有可能跑出正解。
  2. 如何产生新解:
    ①坐标系内:随机生成一个点,或者生成一个向量;
    ②序列问题:random_shuffle或随机交换两个数;
    ③网格问题:看作二维序列,每次交换两个格子。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值