题解/算法 {524. 愤怒的小鸟}

题解/算法 {524. 愤怒的小鸟}

LINK: https://www.acwing.com/problem/content/description/526/;

题意中, 不用管录入的m 他只是一种提示信息;

这道题的难点, 在于你对数学方程的理解;

令泛型方程为 y = A x 2 + B x ( A > 0 ) y = Ax^2 + Bx (A>0) y=Ax2+Bx(A>0);

第一象限里 有若干个不同的点 (x: >0, y: >0), 至少需要几个泛型方程 使得可以经过所有的点;

首先我们分析下答案, 答案中任意一个泛型方程 (即抛物线) 一定经过 ≥ 1 \geq 1 1个点;
即, 虽然泛型方程 有无数个, 但我们当然只关注 那些会经过点的方程;

这些会经过点的方程, 再分为2类: (1: 只会经过1个点) (2: 经过 ≥ 2 \geq 2 2个点);

对于第1类, 他只会有 N N N个 (因为有N个点);

关键是看第2类, LINK: (https://editor.csdn.net/md/?articleId=129194272)-(@LOC_2), 对于任意一条 经过了a1,a2,...若干个点的方程E, 这个方程 可以通过任意两个点ai, aj 来表示, 也就是只要我们得到ai, aj, 就可以得到这个方程E的具体参数(即 A , B A,B A,B的值);
. 因为一共有 N N N个点, 那么这类方程 只会有 N 2 N^2 N2个 (因为2点就可以确定一个方程);

即, 此时 我们得到了若干个方程, 每个方程 会经过哪些点 我们用一个长度为N的2进制来表示;
对于第1类方程: 因为我们假设了 他只会经过1个点 (这是个前提), 因此 他就对应1 << i这个状态 (即其二进制一定只有一个1);
对于第2类方程: 虽然他由2个点可以确定, 但是他可不一定只经过2个点, 他会经过 ≥ 2 \geq 2 2的点, 因此 我们得到 A , B A,B A,B这个具体方程后 再遍历所有点 看这个方程 经过了那些点;

因此, 这就变成一个重复覆盖问题, 使用状压DP的算法模板, LINK: https://editor.csdn.net/md/?articleId=131009780;
因此, 本题的难点 就在于 你构造这个重复覆盖问题的01矩阵的过程, 即获得所有方程;

注意点:
通过方程上的任意2个点 就可以确定该方程, 这是正确的;
但是, 通过任意2个点 不一定可以确定一个方程, 因为他对应求解线性方程组, 假如解是(无解 或 有无穷个解), 那么此时 他就不能确定一个方程 (比如这两个点是垂直的, 他的行列式是0, 根据克莱姆法则, 肯定不是唯一解);
. LINK: https://editor.csdn.net/md/?articleId=130156817;

还有一点, 你很容易给忽略掉…
题目要求的泛型方程, 要满足 A < 0 A<0 A<0的, 你即便得到唯一解, 他可能是形如 A ≥ 0 A\geq 0 A0的, 因此 你还要特判, 即对结果 A , B A,B A,B 判断是否 A < 0 A<0 A<0;

代码

pair< double, double> A[ 21];
set< int> STs[ 21];
int DP[ 1 << 18];
int Power_to_bit[ 1 << 18];

bool Get_equation( int _i, int _j, double & _A, double & _B){
    double a11 = A[_i].first * A[_i].first, a12 = A[_i].first, b1 = A[_i].second;
    double a21 = A[_j].first * A[_j].first, a22 = A[_j].first, b2 = A[_j].second;
    double det_A = a11 * a22 - a12 * a21;
    
    if( Tools::Double_equal( det_A, 0)){ // 特判
        return false;
    }
    
    double det_x = b1 * a22 - a12 * b2;
    double det_y = a11 * b2 - b1 * a21;

    _A = det_x / det_A, _B = det_y / det_A;
    
    if( Tools::Double_greaterEqual( _A, 0)){ // 特判
        return false;
    }
    
    return true;
}

void __Solve(){
    int N, __t;  cin>> N>> __t;
    for( int i = 0; i < N; ++i) cin>> A[ i].first>> A[ i].second;


    for( int i = 0; i < N; ++i){
        auto & curSTs = STs[ i];
        curSTs.clear();

        curSTs.insert( 1 << i); // 第1类方程

        for( int j = 0; j < N; ++j){ // 第2类方程
            double A, B; // y = A x^2 + B x
            if( false == Get_equation( i, j, A, B)){
                continue;
            }

            int st = 0;
            for( int k = 0; k < N; ++k){
                if( Tools::Double_equal( A * ::A[k].first * ::A[k].first + B * ::A[k].first, ::A[k].second)){
                    st |= (1 << k);
                }
            }
            curSTs.insert( st);
        }
    }
    
    重复覆盖的 状压DP模板;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值