平衡点——搜索
题目来源
题目描述
如图:有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 轴正方向移动,而当前移动是将原点像 x 轴负方向上移动,这说明原点的横坐标一定在这两次假设的原点的横坐标中间,因此我们缩小以后原点移动的距离。
当两次移动的坐标差在一定的范围之内时,说明我们得到了解,输出即可(注意:规定的范围一定要小于题目所要求的精度范围)
源代码
#include <iostream>
#include <cmath>
#include <cstdio>
#define abs(x) ((x) >= 0 ? (x) : (-(x)))
using namespace std;
int n;
double x, y;
bool XF = true, YF = true;//原点移动的方向 true 代表正方向,false 代表负方向
struct Position {
int x;
int y;
int power;
} positions[1005];
void solve(double move) {
double X, Y, temp;
X = Y = 0;
for (int i = 1; i <= n; i++) {
temp = sqrt((x - positions[i].x) * (x - positions[i].x) + (y - positions[i].y) * (y - positions[i].y));//记录该点到原点的欧几里得距离
if (temp == 0)//判断是否与原点重合
continue;
//若不重合则进行正交分解
X += positions[i].power / temp * (positions[i].x - x);
Y += positions[i].power / temp * (positions[i].y - y);
}
temp = sqrt(X * X + Y * Y);//计算正交分解后的合力
//将原点在合力方向上位移一定距离
x += move / temp * X;
y += move / temp * Y;
}
int main() {
freopen("in.txt", "r", stdin);
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d%d%d", &positions[i].x, &positions[i].y, &positions[i].power);
//move:移动的步长 tx ty 保存移动前的原点的位置
double move = 5000, tx, ty;
while (true) {
//记录移动前原点的位置
tx = x;
ty = y;
solve(move);//按照步长移动原点
if (abs(tx - x) < 0.00001 && abs(ty - y) < 0.00001)//判断是否满足精度要求
break;
if ((XF != (x > tx)) || (YF != (y > ty))) {//若移动方向发生改变
//记录移动方向
XF = !x > tx;
YF = !y > ty;
move = move * 0.9;//缩小移动距离
}
}
printf("%.3f %.3f", x, y);
return 0;
}