( 其他算法与技巧 )【 模拟退火 】

( 其他算法与技巧 )【 模拟退火 】

 

先看题!!

题目描述

如图:有n个重物,每个重物系在一条足够长的绳子上。每条绳子自上而下穿过桌面上的洞,然后系在一起。图中X处就是公共的绳结。假设绳子是完全弹性的(不会造成能量损失),桌子足够高(因而重物不会垂到地上),且忽略所有的摩擦。

问绳结X最终平衡于何处。

注意:桌面上的洞都比绳结X小得多,所以即使某个重物特别重,绳结X也不可能穿过桌面上的洞掉下来,最多是卡在某个洞口处。

输入格式

文件的第一行为一个正整数n(1≤n≤1000),表示重物和洞的数目。接下来的n行,每行是3个整数:Xi.Yi.Wi,分别表示第i个洞的坐标以及第 i个重物的重量。(-10000≤x,y≤10000, 0<w≤1000 )

输出格式

你的程序必须输出两个浮点数(保留小数点后三位),分别表示处于最终平衡状态时绳结X的横坐标和纵坐标。两个数以一个空格隔开。

输入输出样例

输入 #1

3
0 0 1
0 2 1
1 1 1

输出 #1

0.577 1.000

 

分析: 模拟退火最重要的就是判断那种情况是最优的,所以我们需要找到一个标准来判断当前情况的最优率为多少。

此题的最优情况是,\sum所选点到重物点的距离*重物的重量,最小即最优。就是代码中的energy函数。

代码:

#include <bits/stdc++.h>
#define down 0.996 // 徐徐降温

using namespace std;

struct node {
    int x,y,w;
}object[2005];// 存物体的坐标和重量
int n;
double ansx,ansy,answ; // 最终答案

double energy( double x, double y )//根据题意变化, 能量总和越小越稳定( 即越接近正确答案 )
{
    double r=0,dx,dy;
    for ( int i=1; i<=n; i++ ) {
        dx = x-object[i].x;
        dy = y-object[i].y;
        r += sqrt(dx*dx+dy*dy)*object[i].w;
    }
    return r;
}

void sa()
{
    double t = 3000; // 温度足够高
    while ( t>1e-15 ) {
        double ex = ansx + (rand()*2-RAND_MAX)*t; // 随机产生新答案.
        double ey = ansy + (rand()*2-RAND_MAX)*t;
        double ew = energy(ex,ey);
        double de = ew - answ;
        if ( de<0 ) { // 新答案能量低,更稳定
            ansx = ex;
            ansy = ey;
            answ = ew;
        }
        else if ( exp(-de/t)*RAND_MAX>rand() ) { // 否则根据多项式概率接受
            ansx = ex;
            ansy = ey;
        }
        t *= down;
    }
}

void solve() // 多跑几遍退火,增加得到最优解的概率
{
    sa();sa();sa();sa();
}

int main()
{
    cin >> n;
    for ( int i=1; i<=n; i++ ) {
        scanf("%d %d %d",&object[i].x,&object[i].y,&object[i].w);
        ansx += object[i].x;
        ansy += object[i].y;
    }
    ansx/=n; ansy/=n; // 以平均值作为初始答案
    answ = energy(ansx,ansy);
    solve();
    printf("%.3f %.3f\n",ansx,ansy);

    return 0;
}

 

再来一道题:Gym - 101981D

 

题意:三维空间中有若干的城市,现在需要找到一个基站位置,要求此点到所有城市的最远距离最小。

分析: 模拟退火来找最优点,energy函数定义为for循环找距离的最大值。

代码:

#include <bits/stdc++.h>
#define down 0.996 // 徐徐降温

using namespace std;

struct node {
    int x,y,z,w;
}object[2005];// 存物体的坐标和重量
int n;
double ansx,ansy,ansz,answ; // 最终答案

double energy( double x, double y, double z )//根据题意变化, 能量总和越小越稳定( 即越接近正确答案 )
{
    double r=0,dx,dy,dz;
    for ( int i=1; i<=n; i++ ) {
        dx = x-object[i].x;
        dy = y-object[i].y;
        dz = z-object[i].z;
        r = max(r,sqrt(dx*dx+dy*dy+dz*dz)); // 找最大距离,越小越接近正确答案
    }
    return r;
}

void sa()
{
    double t = 3000; // 温度足够高
    while ( t>1e-15 ) {
        double ex = ansx + (rand()*2-RAND_MAX)*t; // 随机产生新答案.
        double ey = ansy + (rand()*2-RAND_MAX)*t;
        double ez = ansz + (rand()*2-RAND_MAX)*t;
        double ew = energy(ex,ey,ez);
        double de = ew - answ;
        if ( de<0 ) { // 新答案能量低,更稳定
            ansx = ex;
            ansy = ey;
            ansz = ez;
            answ = ew;
        }
        else if ( exp(-de/t)*RAND_MAX>rand() ) { // 否则根据多项式概率接受
            ansx = ex;
            ansy = ey;
            ansz = ez;
        }
        t *= down;
    }
}

void solve() // 多跑几遍退火,增加得到最优解的概率
{
    sa();sa();sa();sa();
}

int main()
{
    cin >> n;
    for ( int i=1; i<=n; i++ ) {
        scanf("%d %d %d",&object[i].x,&object[i].y,&object[i].z);
        ansx += object[i].x;
        ansy += object[i].y;
        ansz += object[i].z;
    }
    ansx/=n; ansy/=n; ansz/=n; // 以平均值作为初始答案
    answ = energy(ansx,ansy,ansz);
    solve();
    printf("%.15f\n",answ);

    return 0;
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值