P1337 [JSOI2004]平衡点 吊打XXX - 模拟退火

P1337 [JSOI2004]平衡点 吊打XXX

模拟退火

初始温度\(T_0\) 终止温度\(T_k\) 温度变化率\(d\)

\(T_k\)略大于0,\(d\)略小于1

当前状态\(x,y\) 当前解\(E\) 当前最优解\(minE\) 当前温度\(T\)

新状态\(nx,ny\) 新解\(nE\) 新解与当前解差值\(\Delta E = nE-E\)

\(if\)新解比当前解更优\(nE<E\)

当前状态\(x,y\)移动到 \(nx,ny\)

当前解\(E\)移动到\(nE\)

$else \(我们有\)e^{\frac{\Delta E}{kT}}\(的概率使当前状态移动到\)nx,ny\((其中\)k\(为随机数) 当前温度\)T\(降至\)T*d$

最后\(T\)降到等于低于\(T_k\)时,算法结束。

Tips:

1.\(nx,ny\)可通过将\(x,y\)加上一个\([-T,T]内的随机数(T*(Rand()*2-1))\)得到。

其中Rand()函数如下:

inline double Rand(){
    return double(rand())/double(RAND_MAX);
}

2.判断当前状态是否能移动到新状态

(本题求的是最小值,因此当\(\Delta E = nE-E\)<0时更优)

inline bool Accept(double delta,double T){
    return delta<0||Rand()<exp(-delta/T);
}

3.这是个看脸的算法,这是个玄学的算法。(参见代码中的八位大质数和八遍退火)

4.别学我拿两个退火对拍。。。

AC CODE:(Warning : 本题不保证每次都能AC)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<ctime>
#include<cstdlib>
#include<cmath>
using namespace std;
const int N = 1000 + 10;
int n;
struct node{
    double x,y,w;
}a[N];
node Ans;
double minE;
inline double dis(double x,double y,double _x,double _y){
    return sqrt((x-_x)*(x-_x)+(y-_y)*(y-_y));
}
inline double cal(double x,double y){
    double ans=0;
    for(int i=1;i<=n;i++){
        double _x=a[i].x,_y=a[i].y;
        ans+=a[i].w*dis(x,y,_x,_y);
    }
    if(ans<minE){
        minE=ans;
        Ans.x=x,Ans.y=y;
    }
    return ans;
}
inline double Rand(){
    return double(rand())/double(RAND_MAX);
}
inline bool Accept(double delta,double T){
    return delta<0||Rand()<exp(-delta/T);
}
void SA(node poi,double T0,double Tk,double d){
    double x=poi.x,y=poi.y;//当前坐标
    double E=cal(x,y);//当前解
    minE=E;//最优解
    double T=T0;//当前T
    while(T>Tk){
        double nx=x+T*(Rand()*2-1),ny=y+T*(Rand()*2-1);
        //新坐标 T*(Rand()*2-1) 生成[-T,T]内的实数
        double nE=cal(nx,ny);//新解
        if(Accept(nE-E,T)){//转移
            x=nx,y=ny;
            E=nE;
        }
        T*=d;//get low
    }
    for(int i=1;i<=1000;i++){
        x=Ans.x+T*(Rand()*2-1),y=Ans.y+T*(Rand()*2-1);
        cal(x,y);
    }
}
int main(){
//  freopen("data.in","r",stdin);
//  freopen("sol.out","w",stdout);
    srand(19260817);srand(rand());srand(rand());
    scanf("%d",&n);
    node _;_.x=0,_.y=0;
    for(int i=1;i<=n;i++){
        double x,y,w;scanf("%lf%lf%lf",&x,&y,&w);
        a[i]=(node){x,y,w};
        _.x+=x,_.y+=y;
    }
    _.x/=n,_.y/=n;
    double T0=2333,Tk=1e-3,d=1-7e-2;
    SA(_,T0,Tk,d);
    SA(Ans,T0,Tk,d);
    SA(Ans,T0,Tk,d);
    SA(Ans,T0,Tk,d);
    SA(Ans,T0,Tk,d);
    SA(Ans,T0,Tk,d);
    SA(Ans,T0,Tk,d);
    SA(Ans,T0,Tk,d);
    printf("%.3lf %.3lf\n",Ans.x,Ans.y);
    return 0;
}

原文地址:https://www.cnblogs.com/Loi-Brilliant/p/9780923.html

时间: 2024-11-05 12:06:15

P1337 [JSOI2004]平衡点 吊打XXX - 模拟退火的相关文章

P1337 [JSOI2004]平衡点 / 吊打XXX

题目描述 如图:有n个重物,每个重物系在一条足够长的绳子上.每条绳子自上而下穿过桌面上的洞,然后系在一起.图中X处就是公共的绳结.假设绳子是完全弹性的(不会造成能量损失),桌子足够高(因而重物不会垂到地上),且忽略所有的摩擦. 问绳结X最终平衡于何处. 注意:桌面上的洞都比绳结X小得多,所以即使某个重物特别重,绳结X也不可能穿过桌面上的洞掉下来,最多是卡在某个洞口处. 输入输出格式 输入格式: 文件的第一行为一个正整数n(1≤n≤1000),表示重物和洞的数目.接下来的n行,每行是3个整数:Xi

洛谷P1337 【[JSOI2004]平衡点 / 吊打XXX】(模拟退火)

洛谷题目传送门 很可惜,充满Mo力的Mo拟退火并不是正解.不过这是一道最适合开始入手Mo拟退火的好题. 对模拟退火还不是很清楚的可以看一下 这道题还真和能量有点关系.达到平衡稳态的时候,物体的总能量应该是最小的.而总的能量来源于每个物体的重力势能之和.要想让某个物体势能减小,那就让拉着它的绳子在桌面下方的长度尽可能的长,也就是桌面上的要尽可能短.由此看来,某个物体的势能与桌面上的绳子的长度.物体重量都成正比. 于是,为了找到平衡点,我们要找一个点使得\(\sum_{i=1}^n d_i*w_i\

[luogu1337][bzoj3680][JSOI2004]平衡点 / 吊打XXX

题目描述 gty又虐了一场比赛,被虐的蒟蒻们决定吊打gty.gty见大势不好机智的分出了n个分身,但还是被人多势众的蒟蒻抓住了.蒟蒻们将n个gty吊在n根绳子上,每根绳子穿过天台的一个洞.这n根绳子有一个公共的绳结x.吊好gty后蒟蒻们发现由于每个gty重力不同,绳结x在移动.蒟蒻wangxz脑洞大开的决定计算出x最后停留处的坐标,由于他太弱了决定向你求助. 不计摩擦,不计能量损失,由于gty足够矮所以不会掉到地上. 分析 人生中第一道模拟退火题目,感觉模拟退火这个算法非常的优美又非常的(粗鄙之

[JSOI2004]平衡点 / 吊打XXX

考虑模拟退火. 题目要我们找到一个点,使得整个系统平衡. 这个要求等价于让我们找到一个点,使得系统总能量最小. 我们退火出一个点,然后计算其能量即可. 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<queue> 6 #include<cstdlib> 7 #include<cmath>

【BZOJ3680】吊打XXX 模拟退火

[BZOJ3680]吊打XXX Description gty又虐了一场比赛,被虐的蒟蒻们决定吊打gty.gty见大势不好机智的分出了n个分身,但还是被人多势众的蒟蒻抓住了.蒟蒻们将n个gty吊在n根绳子上,每根绳子穿过天台的一个洞.这n根绳子有一个公共的绳结x.吊好gty后蒟蒻们发现由于每个gty重力不同,绳结x在移动.蒟蒻wangxz脑洞大开的决定计算出x最后停留处的坐标,由于他太弱了决定向你求助.不计摩擦,不计能量损失,由于gty足够矮所以不会掉到地上. Input 输入第一行为一个正整数

BZOJ 3680 吊打XXX 模拟退火

首先这题应该改名叫吊打出题人 题目大意:给定n个质点,求重心 这n个质点的重心满足Σ(重心到点i的距离)*g[i]最小 模拟退火的裸题 尼玛交了两篇 死活过不去 各种改参数 最后发现是我的INF不够大 尼玛! 这题INF开0x3f妥妥过不去...起码要max_of _long_long附近才可以 最后写了10188MS,BZOJ倒数第一--这也是种艺术啊0.0 #include<cmath> #include<cstdio> #include<cstring> #inc

BZOJ 3680: 吊打XXX【模拟退火算法裸题学习,爬山算法学习】

3680: 吊打XXX Time Limit: 10 Sec  Memory Limit: 128 MBSec  Special JudgeSubmit: 3192  Solved: 1198[Submit][Status][Discuss] Description gty又虐了一场比赛,被虐的蒟蒻们决定吊打gty.gty见大势不好机智的分出了n个分身,但还是被人多势众的蒟蒻抓住了.蒟蒻们将 n个gty吊在n根绳子上,每根绳子穿过天台的一个洞.这n根绳子有一个公共的绳结x.吊好gty后蒟蒻们发现

【BZOJ3680】吊打XXX(模拟退火)

[BZOJ3680]吊打XXX(模拟退火) 题面 BZOJ 题解 模拟退火... 就是模拟退火 然后这题有毒 各种调参数之后终于\(AC\)了.. 这种题就是玄学呀... 温度要调大 最后跑完还要向四周多\(rand\)几次 保证能够找到最优解... #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<

吊打XXX

3680: 吊打XXX Time Limit: 10 Sec  Memory Limit: 128 MBSec  Special JudgeSubmit: 2670  Solved: 984[Submit][Status][Discuss] Description gty又虐了一场比赛,被虐的蒟蒻们决定吊打gty.gty见大势不好机智的分出了n个分身,但还是被人多势众的蒟蒻抓住了.蒟蒻们将 n个gty吊在n根绳子上,每根绳子穿过天台的一个洞.这n根绳子有一个公共的绳结x.吊好gty后蒟蒻们发现由