P2831 愤怒的小鸟
P2831 愤怒的小鸟
题意:
一个平面上有 \(n\) 个在第一象限的点,求至少要多少个解析式为 \(y=ax^2+bx\) 的开口向下的抛物线(即 \(a<0\))才能覆盖这 \(n\) 个点。
其中,\(n\le18\)。
分析:
先来看如何求抛物线解析式。已知两点 \((x_1,y_1),(x_2,y_2)\),求经过这两个点的抛物线解析式 \(y=ax^2+bx\) 的系数。
由抛物线 \(y=ax^2+bx\) 过点 \((x_1,y_1),(x_2,y_2)\) 得:\(\begin{cases}y_1=ax_1^2+bx_1\\y_2=ax_2^2-bx_2\end{cases}\)
\(\begin{cases}x_2y_1=ax_1^2x_2+bx_1x_2\\x_1y_2=ax_1x_2^2+bx_1x_2\end{cases}\)
由上面的式子减去下面的式子得:
\(x_2y_1-x_1y_2=a(x_1^2x_2-x_1x_2^2)=ax_1x_2(x_1-x_2)\)
\(\therefore a=\dfrac{x_2y_1-x_1y_2}{x_1x_2(x_1-x_2)}\)
\(\because y_1=ax_1^2+bx_1\)
\(\therefore b=\dfrac{y_1-ax_1^2}{x_1}\)
\(\because x_1\ne0\)
\(\therefore b=\dfrac{y_1}{x_1}-ax_1\)
\(\therefore\begin{cases}a=\dfrac{x_2y_1-x_1y_2}{x_1x_2(x_1-x_2)}\\b=\dfrac{y_1-ax_1^2}{x_1}\end{cases}\)
我们预处理出每两个点所在的抛物线,并用 \(n\) 位二进制数记录抛物线上有哪些点(将经过点 \(i,j\) 的抛物线上的点用 \(c_{i,j}\) 来记录),复杂度为 \(O(n^3)\)。
设 \(f_i\) 表示这些点是否已经被覆盖(\(i\) 的第 \(j\) 个二进制位用 \(1/0\) 表示第 \(j\) 个点是/否已经被覆盖)。每次枚举集合 \(i\),其中 \(i\in[0,2^n)\),并枚举两个点 \(j,k\),表示集合 \(i\) 是由一个集合加上 \(j,k\) 所在的抛物线的贡献转移而来的,即 \(f_i=f_{i\cap(c_{j,k}的补集)}+1\)。
按照如上的思路,复杂度为 \(O(T(n^3+2^nn^2))\)(\(T\) 为数据组数),第20个测试点达到了 \(4\times10^8\) 的级别,会 \(T\)。
考虑优化。设 \(x\) 为集合 \(i\) 中的第一个没被经过的点,可以发现无论当前状态下无论是否选择该点,以后都得选,所以会出现一些重复无用的状态。于是我们每次枚举集合 \(i\) 时,就强制选择集合中第一个没被经过的点,因此只需枚举另一个点即可转移状态。
设集合 \(i\) 中第一个没被经过的点为 \(pos\),枚举一个点 \(j(j\in[1,n])\),状态转移为 \(f_i=f_{i\cap(c_{pos,j}的补集)}\),复杂度为 \(O(T(n^3+2^nn))\)。
因为可能一个点单独被覆盖更优的情况,因此每次要单独转移只覆盖点 \(pos\) 的情况。
注意转移时,并非只有 \(c_{pos,j}\in i\) 时才转移,因为可能会出现抛物线相交,导致一条抛物线上的点会被其它抛物线覆盖,但仍需选择这条抛物线去覆盖点的情况。
Code:
#include <bits/stdc++.h>
using namespace std;
const int N = 20, M = 1e6 + 10;
const double eps = 1e-8;
int _, n, m, f[M], bit[M], c[N][N];
double a, b, x[N], y[N];
inline int read(){
int s = 0, w = 1;
char ch = getchar();
for (; ch < '0' || ch > '9'; w *= ch == '-' ? -1 : 1, ch = getchar());
for (; ch >= '0' && ch <= '9'; s = s * 10 + ch - '0', ch = getchar());
return s * w;
}
int lowbit(int x){
return (x & 1) ? 1 : (lowbit(x >> 1) + 1);
}
void calc(double x1, double y1, double x2, double y2){
a = (x2 * y1 - x1 * y2) / x1 / x2 / (x1 - x2);
b = y1 / x1 - a * x1;
}
signed main(){
for (int i = 1; i < (1 << 18); ++i) bit[i] = lowbit(i);
for (scanf("%d", &_); _--; ){
memset(c, 0, sizeof(c));
memset(f, 0x3f, sizeof(f));
f[0] = 0;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) scanf("%lf%lf", &x[i], &y[i]);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j){
if (fabs(x[i] - x[j]) < eps) continue;
calc(x[i], y[i], x[j], y[j]);
if (a > -eps) continue;
for (int k = 1; k <= n; ++k)
if (fabs(a * x[k] * x[k] + b * x[k] - y[k]) < eps) c[i][j] |= (1 << (k - 1));
}
for (int i = 1; i < (1 << n); ++i){
f[i] = min(f[i], f[i & (~(1 << (bit[i] - 1)))] + 1);
for (int j = 1; j <= n; ++j) f[i] = min(f[i], f[i & (~c[bit[i]][j])] + 1);
}
printf("%d\n", f[(1 << n) - 1]);
}
return 0;
}
浙公网安备 33010602011771号