Luogu P1337 [JSOI2004]平衡点 / 吊打XXX 77分题解

题目描述详见https://www.luogu.org/problemnew/lists?name=%E5%B9%B3%E8%A1%A1%E7%82%B9

此题方法灵感源于感知器学习算法。简而言之,就是随机选取数据,然后计算和期望结果相比的损失,在以损失值乘上学习率,更新到数据上。学习率越大,学习速度,也就是调整至期望结果的速度越快,但精度越低。学习率越小,精度越高,但学习时间也越长。所以要选取合适的学习率。时间复杂度没有具体分析过,不过个人实现的分类器在学习率为0.001时训练次数大约为数百万次,耗时几秒,OI中可以接受

所以本题思路大致如下:   读入数据。选取随机坐标。进入while循环,当受力不平衡时更新坐标,平衡则跳出,输出,结束。

判断受力是否平衡的函数思路如下:  把x,y方向的损失值赋值为0。访问所有孔的坐标,对受力情况进行正交分解,加到对应方向的损失值上。若两个损失值都为零返回true,否则返回false

我以vector储存孔的数据及对应重量。在此,因为double类型的精度问题,引入常量eps = 1e-4代替零。注意,损失值有正有负,故判断的应是(LossX < eps && LossX > -eps && LossY < eps && LossY > -eps), 切记记得判断-eps,否则损失值为0了也无法跳出循环。不要问我是怎么知道的

蒟蒻代码:

#include <iostream>
#include <utility>
#include <vector>
#include <cmath>
#include <cstdlib>
#include <cstdio>
#include <ctime>

typedef std::pair<int, int> point_int;
typedef std::pair<double, double> point_db;
const double Eta = 1e-5, eps = 1e-4;
int N = 0;
std::vector<point_int> vec;
std::vector<int> vec_w;
double LossX = 0, LossY = 0;

void init(void);
void solve(void);
int read(void);
bool balanced(const point_db&);
bool equals_zero(double);

int main() {
    init();
    solve();
    return 0;
}

void init(void) {
    N = read();
    for (int i=0; i<N; i++) {
        int x = read(), y = read(), w = read();
        vec.push_back(point_int(x, y));
        vec_w.push_back(w);
    }
    return;
}

void solve(void) {
    double x0 = double(rand())/double(RAND_MAX),
        y0 = double(rand())/double(RAND_MAX);
    point_db p = point_db(x0, y0);
    bool done = balanced(p);

    while (!done) {
        p.first -= (LossX*Eta);
        p.second -= (LossY*Eta);
        done = balanced(p);
    }
    printf("%.3f %.3f\n", p.first, p.second);
}

int read(void) {
    char ch = getchar();
    int res = 0, x = 1;
    while (!isdigit(ch)) {
        if (ch == '-')
            x = -1;
        ch = getchar();
    }
    while (isdigit(ch)) {
        res = res*10 + ch-'0';
        ch = getchar();
    }
    return res*x;
}

bool balanced(const point_db& pos) {
    LossX = LossY = 0.0;
    for (int i=0; i<N; i++) {
        double lx = pos.first-vec[i].first, ly = pos.second-vec[i].second;
        double lc = sqrt(lx*lx + ly*ly);
        double sinx = ly/lc, cosx = lx/lc;
        LossY += (vec_w[i]*sinx);
        LossX += (vec_w[i]*cosx);
    }
    return (LossX < eps && LossX > -eps && LossY < eps && LossY > -eps);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值