P1337 [JSOI2004]平衡點 / 吊打XXX(模擬退火)
阿新 • • 發佈:2019-01-26
(一)題面:
題目描述
如圖:有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; }
(五)總結:
模擬退火練手題(雖然正解不是模擬退火的說~)。