1. 程式人生 > >P1337 [JSOI2004]平衡點 / 吊打XXX(模擬退火)

P1337 [JSOI2004]平衡點 / 吊打XXX(模擬退火)

(一)題面:

題目描述

如圖:有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

(二)題意:

(中文題詳見題面)

(三)題解:

最終的系統穩定時,系統總的能量肯定最低,而總能量最低,就是各個物體的勢能之和最低,那麼也可以轉化為桌面上的<連線各個物體的繩子的長度與其重量的乘積>之和最小。接下來就是非常類似於求費馬了,只不過距離之和變成了<距離乘上一個權值>的和。

模擬退火不懂的可以先看看

模擬退火簡介

(四)程式碼:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<cmath>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#define eps 1e-14
#define delta 0.97
#define Random (T*(2*rand()-RAND_MAX))
#define Drand (long double)rand()/RAND_MAX
using namespace std;
const int maxn=1010;
struct node{
    double x,y,w;
    node(long double _x=0,long double _y=0){x=_x;y=_y;}
    void input(){scanf("%lf%lf%lf",&x,&y,&w);}
    node operator - (const node &n1)const{
        return node(x-n1.x,y-n1.y);
    }
    long double operator * (const node &n1)const{
        return x*n1.x+y*n1.y;
    }
}th[maxn],now,nxt,bst;
long double solve(node p,int n){
    long double res=0;
    for(int i=0;i<n;i++){
        res+=sqrt((p-th[i])*(p-th[i]))*th[i].w;
    }
    return res;
}
int main(){
    #ifdef DanDan
    freopen("in.txt","r",stdin);
    #endif // DanDan
    srand(time(0));
    int n;scanf("%d",&n);
    for(int i=0;i<n;i++){
        th[i].input();
        now.x+=th[i].x;
        now.y+=th[i].y;
    }now.x/=n,now.y/=n;
    long double ans=solve(now,n),bt=1e18;
    for(long double T=100000;T>eps;T*=delta){
            nxt.x=now.x+Random;
            nxt.y=now.y+Random;
            double res=solve(nxt,n);
            if(bt>res)bt=res,bst=nxt;
            if(ans>res||exp((ans-res)/T)>Drand)ans=res,now=nxt;
    }
    printf("%.3f %.3f\n",bst.x,bst.y);
    return 0;
}

(五)總結:

模擬退火練手題(雖然正解不是模擬退火的說~)。