[模板]模擬退火 / 洛谷 P1337 [JSOI2004]平衡點
阿新 • • 發佈:2021-08-20
目錄
[模板]模擬退火 / 洛谷
P1337 [JSOI2004]平衡點
題目
題目描述
如圖:有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 <cstdio> #include <cmath> #include <cstdlib> unsigned seed; int read() { int re = 0; char c = getchar(); bool negt = false; while(c < '0' || c > '9') negt |= (c == '-') , c = getchar(); while(c >= '0' && c <= '9') re = (re << 1) + (re << 3) + c - '0' , c = getchar(); seed *= re; return negt ? -re : re; } const int N = 10010; struct NodeClass { int x , y , m; }node[N]; int n; double ansx , ansy; double minEp = 1e18;//Ep:重力勢能的縮寫,minEp即最小的重力勢能,也就是(搜過的答案中)最穩定的狀態,最趨近平衡的狀態 const double delta_t = 0.993; const double originT = 3000;//原溫度 double calc_Ep(double nowx , double nowy) { double sum = 0; for(int i = 1 ; i <= n ; i++) { double delx = nowx - node[i].x , dely = nowy - node[i].y; sum += std::sqrt(delx * delx + dely * dely) * node[i].m;//求總重力勢能,嚴格來說Ep=mgh,這裡省略重力加速度g } return sum; } void simulate_anneal() { double x = ansx , y = ansy; double t = originT; while(t > 1e-14) { double nowx = ansx + (rand() * 2 - RAND_MAX) * t; double nowy = ansy + (rand() * 2 - RAND_MAX) * t; double nowEp = calc_Ep(nowx , nowy); double DE = nowEp - minEp; if(DE < 0) {//新答案更優 ansx = x = nowx , ansy = y = nowy; minEp = nowEp; } else if(exp(-DE / t) * RAND_MAX > rand()) {//隨機決定是否選擇不那麼優的答案 x = nowx , y = nowy; } t *= delta_t; } } int main() { n = read(); for(int i = 1 ; i <= n ; i++) node[i].x = read() , node[i].y = read() , node[i].m = read(); std::srand(seed); for(int i = 1 ; i <= 4 ; i++)//多跑幾次 simulate_anneal(); printf("%.3f %.3f" , ansx , ansy); return 0; }