题目描述
如图:有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
思路:
本题是一道物理题,根据物理学的知识,当一个系统处于平衡状态时,系统的总能量是最小的,根据图示我们可以判断,系统的总能量其实就是各个物体的重力势能和,而各个物体的质量是一定的,这就要求物体离桌子越远,离地越近
换句话说,也就是要找一个点 x,使得这个点到桌面上给出的 n 个点的距离与质量和最小,即:
我们可以用模拟退火的方法来做这个题
首先,将 (0,0) 设为初始答案,然后不断选取一个坐标,比较选择该点时的代价与当前的代价:
- 如果小于当前答案的代价,更新答案
- 如果大于当前答案的代价,有一定概率更新答案
根据模拟退火算法,更新答案的概率,随着时间的增大与代价差的增大而减小,更新坐标的幅度同样随着时间的增大而减小
然后WA的话就多交几遍看 RP 了。。。
源代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<bitset>
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define LL long long
#define Pair pair<int,int>
LL quickPow(LL a,LL b){ LL res=1; while(b){if(b&1)res*=a; a*=a; b>>=1;} return res; }
LL multMod(LL a,LL b,LL mod){ a%=mod; b%=mod; LL res=0; while(b){if(b&1)res=(res+a)%mod; a=(a<<=1)%mod; b>>=1; } return res%mod;}
LL quickMultPowMod(LL a, LL b,LL mod){ LL res=1,k=a; while(b){if((b&1))res=multMod(res,k,mod)%mod; k=multMod(k,k,mod)%mod; b>>=1;} return res%mod;}
LL quickPowMod(LL a,LL b,LL mod){ LL res=1; while(b){if(b&1)res=(a*res)%mod; a=(a*a)%mod; b>>=1; } return res; }
LL getInv(LL a,LL mod){ return quickPowMod(a,mod-2,mod); }
LL GCD(LL x,LL y){ return !y?x:GCD(y,x%y); }
LL LCM(LL x,LL y){ return x/GCD(x,y)*y; }
const double EPS = 1E-15;
const int MOD = 1000000000+7;
const int N = 1000+5;
const int dx[] = {0,0,1,-1,1,1,-1,-1};
const int dy[] = {1,-1,0,0,1,-1,1,-1};
using namespace std;
struct Node{
int x, y;
int w;
} node[N];
int n;
double getDis(double x1,double y1,double x2,double y2){
return sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
}
double getRes(double x, double y) {//计算n个到选定(x,y)的距离乘以重量之和
double res = 0.0;
for (int i = 1; i <= n; i++)
res += (double)getDis(x, y, node[i].x, node[i].y) * node[i].w;
return res;
}
void SA(double &x, double &y) {
double T = 3000;//初始温度
double delta = 0.99;
double res = getRes(x, y);//当前状态结果
while (T > EPS) {
double nx = x + (rand() * 2 - RAND_MAX) * T;//转移x状态
double ny = y + (rand() * 2 - RAND_MAX) * T;//转移y状态
double newRes = getRes(nx, ny);新状态下的结果
double pr = exp((res - newRes) / T) * RAND_MAX;
if (newRes < res || pr > rand()) {
x = nx;//更新x状态
y = ny;//更新y状态
res = newRes;
}
T *= delta;
}
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
scanf("%d%d%d", &node[i].x, &node[i].y, &node[i].w);
srand(time(0));
srand(rand());
srand(rand());
double x = 0.0, y = 0.0;//初始状态
for (int i = 1; i <= 10; i++)//模拟退火10次
SA(x, y);
printf("%.3lf %.3lf", x, y);
return 0;
}