[JSOI2004]平衡點

原題連接
關於模擬退火的詳細介紹,能夠peng-ym關於模擬退火的介紹。html

題目描述

  • 如圖:有n個重物,每一個重物系在一條足夠長的繩子上。每條繩子自上而下穿過桌面上的洞,而後系在一塊兒。圖中X處就是公共的繩結。假設繩子是徹底彈性的(不會形成能量損失),桌子足夠高(於是重物不會垂到地上),且忽略全部的摩擦。
  • 問繩結X最終平衡於何處。
  • 注意:桌面上的洞都比繩結X小得多,因此即便某個重物特別重,繩結X也不可能穿過桌面上的洞掉下來,最可能是卡在某個洞口處。

輸入輸出格式

  • 輸入格式:
    文件的第一行爲一個正整數n(1≤n≤1000),表示重物和洞的數目。接下來的n行,每行是3個整數:Xi.Yi.Wi,分別表示第i個洞的座標以及第 i個重物的重量。(-10000≤x,y≤10000, 0<w≤1000 )
  • 輸出格式:
    你的程序必須輸出兩個浮點數(保留小數點後三位),分別表示處於最終平衡狀態時繩結X的橫座標和縱座標。兩個數以一個空格隔開。

解題思路

  • 這題怕不是OI中爲數很少的與物理有關係的題。(233)
  • 題目詢問的是繩結最終平衡於何處?
  • 根據物理的知識,當系統處於平衡狀態時,系統的總能量最小。
  • 又此時系統的總能量是等於各個物體的重力勢能,在質量必定時,即要求物體離地最近,離桌子最遠。
  • 那麼,也就是繩子在桌子上的距離儘可能的小。即:\(\sum_{i=1}^{n}m_i*dist_{i,x}\)最小。
  • 模擬退火要解決的問題就是找到這一個點的位置。
  • 模擬退火最主要的參數有幾個:\(T_0\)初始溫度,\(t\)每一次降低的溫度,\(ans\)目前爲止最優的答案,\(now\)新的狀態,\(delta\)當前答案與最優答案的差值。
  • 在擴展狀態時有一個小方法:\((rand()*2-RANDMAX)*T\)。這樣的原理是\((rand()*2-RANDMAX)\)的範圍是從負數到正數的,這樣子在擴展座標的時候就能夠多方向擴展,不會只在一個方向上更新。
  • (PS:還有一個很重要的問題,玄學調參。這種問題最好在能夠看到評測結果的OJ上交,否則你不會知道是本身打錯了,仍是參數沒調好。。。。。。)node

    直接上代碼:

#include<bits/stdc++.h>
#define N 2000
using namespace std;
template<typename T>inline void read(T &x)
{
    x=0;
    static int p;p=1;
    static char c;c=getchar();
    while(!isdigit(c)){if(c=='-')p=-1;c=getchar();}
    while(isdigit(c)) {x=(x<<1)+(x<<3)+(c-48);c=getchar();}
    x*=p;
}
struct node
{
    double x,y,w;
}e[N];
int n;
double ansx,ansy;
const double eps=1e-15;
double f(double x,double y)
{
    double tot=0;
    for(int i=1;i<=n;i++)
    {
        double delx=x-e[i].x;
        double dely=y-e[i].y;
        tot+=sqrt(delx*delx+dely*dely)*e[i].w;
    }
    return tot;
}
void mnth()
{
    double T=200;
    while(T>eps)
    {
        double nowx=ansx+(rand()*2-RAND_MAX)*T;
        double nowy=ansy+(rand()*2-RAND_MAX)*T;
        double delta=f(nowx,nowy)-f(ansx,ansy);
        if(delta<0)ansx=nowx,ansy=nowy;
        else if(exp(-delta/T)*RAND_MAX>rand())ansx=nowx,ansy=nowy;
        T*=0.998;
    }
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("mnth.in","r",stdin);
    freopen("mnth.out","w",stdout);
#endif
    srand((int)time(NULL));
    read(n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lf%lf%lf",&e[i].x,&e[i].y,&e[i].w);
        ansx+=e[i].x;ansy+=e[i].y;
    }
    ansx/=(double)n;ansy/=(double)n;
    mnth();
    printf("%.3lf %.3lf\n",ansx,ansy);
    return 0;
}
相關文章
相關標籤/搜索