[LuoguP1337] [JSOI2004]平衡点 / 吊打XXX

洛谷传送门

题目描述

如图:有n个重物,每个重物系在一条足够长的绳子上。每条绳子自上而下穿过桌面上的洞,然后系在一起。图中X处就是公共的绳结。假设绳子是完全弹性的(不会造成能量损失),桌子足够高(因而重物不会垂到地上),且忽略所有的摩擦。

问绳结X最终平衡于何处。

注意:桌面上的洞都比绳结X小得多,所以即使某个重物特别重,绳结X也不可能穿过桌面上的洞掉下来,最多是卡在某个洞口处。

[LuoguP1337] [JSOI2004]平衡点 / 吊打XXX_第1张图片

输入输出格式
输入格式:

文件的第一行为一个正整数n( 1n1000 1 ≤ n ≤ 1000 ),表示重物和洞的数目。接下来的n行,每行是3个整数:Xi.Yi.Wi,分别表示第i个洞的坐标以及第 i个重物的重量。( 10000x − 10000 ≤ x , y10000 y ≤ 10000 , 0<w1000 0 < w ≤ 1000 )

输出格式:

你的程序必须输出两个浮点数(保留小数点后三位),分别表示处于最终平衡状态时绳结X的横坐标和纵坐标。两个数以一个空格隔开。

输入输出样例
输入样例#1:

3
0 0 1
0 2 1
1 1 1

输出样例#1:

0.577 1.000

解题分析

物理题的既视感…分析一下就会发现当我们随机取一个点, 假设这个点为平衡点时,这个点受到的合力方向即为此点到真正平衡点的方向, 所以我们运用退火算法将随意得到的一个点逼近最终的平衡点。

代码如下:

#include 
#include 
#include 
#include 
#include 
#include 
#define R register
#define W while
#define IN inline
#define gc getchar()
namespace Geometry
{
    #define EPS 1e-8
    #define db double
    struct pt
    {
        db x, y;
        int wei;
    };
    pt data[1005];
    IN pt operator + (const pt &x, const pt &y){return (pt){x.x + y.x, x.y + y.y};}
    IN pt operator - (const pt &x, const pt &y){return (pt){x.x - y.x, x.y - y.y};}
    IN db dis (const pt &x){return sqrtl(x.x * x.x + x.y * x.y);}
    db dist = 5000;
    db x, y, pre_x, pre_y;
    int dot;
    bool DX = true, DY = true;
    IN void move()
    {
        db X = 0, Y = 0, D, lef, up; 
        for (R int i = 1; i <= dot; ++i)
        {//使用cos值进行正交分解并乘以其质量(权重)
            D = dis((pt){x - data[i].x, y - data[i].y});
            if(!D) continue;
            X += (data[i].x - x) / D * data[i].wei;
            Y += (data[i].y - y) / D * data[i].wei;
        }
        D = sqrtl(X * X + Y * Y);
        x += X / D * dist;//得到新点
        y += Y / D * dist;
    }
}
using namespace Geometry;
int main()
{
    scanf("%d", &dot);
    for (R int i = 1; i <= dot; ++i)
    {
        scanf("%lf%lf%d", &data[i].x, &data[i].y, &data[i].wei);
    }
    x = 0, y = 0;
    W (233)
    {
        pre_y = y;
        pre_x = x;
        move();
        if (fabs(pre_x - x) < 0.00001 && fabs(pre_y - y) < 0.00001) break;//到达指定精度, 直接输出
        if(DX != (x > pre_x) || DY != (y > pre_y))
        {//方向改变, 进行退火操作
            DX = !x > pre_x;
            DY = !x > pre_y;
            dist*= 0.8;
        }
    }
    printf("%.3lf %.3lf", x, y);
    return 0;
}
坑点分析

第一遍打的时候并没有用cos值来正交分解, 直接用了tan值, 导致WA的飞起来…显然单位值应该是斜边上的力。

你可能感兴趣的:(爬山算法)