题解/算法 {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
A≥0的, 因此 你还要特判, 即对结果
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模板;
}