模拟退火
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说明/提示
[JSOI]
1 // luogu-judger-enable-o2 2 // luogu-judger-enable-o2 3 4 #include<bits/stdc++.h> 5 #define re register int 6 #define down 0.9986 7 using namespace std; 8 int n; 9 const int maxn=1000+5; 10 double ansx,ansy,answ; 11 int x[maxn],y[maxn],w[maxn]; 12 double ener(double nx,double ny) 13 { 14 double sum=0,dx,dy; 15 for(re i=1;i<=n;i++) 16 { 17 dx=nx-x[i]; 18 dy=ny-y[i]; 19 sum+=(sqrt(dx*dx+dy*dy))*w[i]; 20 } 21 return sum; 22 } 23 void SA() 24 { 25 double t=3000; 26 while(t>1e-15) 27 { 28 double ax=ansx+(rand()*2-RAND_MAX)*t; 29 double ay=ansy+(rand()*2-RAND_MAX)*t; 30 double ans=ener(ax,ay); 31 double del=ans-answ; 32 if(del<0) 33 { 34 answ=ans; 35 ansx=ax; 36 ansy=ay; 37 } 38 else if(exp(-del/t)*RAND_MAX>rand()) 39 { 40 ansx=ax; 41 ansy=ay; 42 } 43 t*=down; 44 } 45 46 } 47 void work() 48 { 49 SA(); 50 SA(); 51 SA(); 52 SA(); 53 SA(); 54 } 55 56 int main() 57 { 58 cin>>n; 59 for(re i=1;i<=n;i++) 60 { 61 cin>>x[i]>>y[i]>>w[i]; 62 ansx+=x[i]; 63 ansy+=y[i];} 64 ansx/=n; 65 ansy/=n; 66 answ=ener(ansx,ansy); 67 work(); 68 printf("%.3lf %.3lf\n",ansx,ansy); 69 return 0; 70 }