Problem
平面上有(n)个在第一象限的点,求至少要用几个类似于(y = ax^2 + bx(a < 0,a,b in mathbb{R}))的抛物线能将其全部覆盖。
(n le 18)
Solution
Thinking 1
(1 le n le 18),状压dp.
设(dp[S])为S状态下至少要用几个抛物线。
Thinking 2
发现抛物线必经((0,0))。所以再选两点即可确定抛物线。
具体来说:现在有两个点((x_1,y_1),(x_2,y_2)),那么得:
[egin{cases}
ax_1^2 + bx_1 = y_1\
ax_2^2 + bx_2 = y_2\
end{cases}
]
1式乘上(x_2),2式乘上(x_1),得
[ax_1^2x_2 + bx_1x_2 = y_1x_2\
ax_2^2x_1 + bx_1x_2 = y_2x_1
]
相减
[a(x_1^2x_2 - x_2^2x_1) = y_1x_2 - y_2x_1\
a = dfrac{y_1x_2 - y_2x_1}{x_1^2x_2 - x_2^2x_1}
]
(b)同理吧。
Thinking 3
考虑如何求(dp[S]):
- 若(dp[S])的个数<2,则$dp[S] = $个数。
- otherwise,(dp[S] = min{dp[S - s]} + 1)
其中(s)是指:
在(S)中选两点((x_i,y_i),(x_j,y_j))
首先判断其确定的表达式中的(a)是否小于0
然后求出(k),满足(i eq j eq k)且((x_k,y_k))在这个抛物线,(s)加上(2^{k - 1})
注意s要加入i,j
做完了。
Thinking 4
其实我感觉用那个三点式和三分瞎搞搞也是能做的。
# include <bits/stdc++.h>
using namespace std;
# define int long long
const int N = 22;
const double eps = 1e-8;
int T,n,m;
struct node
{
double x,y;
node() {}
node(double _x,double _y) : x(_x),y(_y) {}
}a[N];
int dp[(1 << 20) + 5];
double f(double x,double x1,double x2,double x3,double y1,double y2,double y3)
{
return (((x - x2) * (x - x3)) / ((x1 - x2) * (x1 - x3))) * y1 + (((x - x1) * (x - x3)) / ((x2 - x1) * (x2 - x3))) * y2 + (((x - x1) * (x - x2)) / ((x3 - x1) * (x3 - x2))) * y3;
}
void equ(double &a,double &b,double x1,double x2,double y1,double y2)
{
a = -((y1 * x2 - y2 * x1) / (x2 * x2 * x1 - x1 * x1 * x2));
b = ((y1 * x2 * x2) - (y2 * x1 * x1)) / (x1 * x2 * x2 - x1 * x1 * x2);
return;
}
signed main(void)
{
// freopen("2831.in","r",stdin);
// freopen("2831.out","w",stdout);
scanf("%lld",&T);
while(T--)
{
scanf("%lld%lld",&n,&m);
for(int i = 1; i <= n; i++)
{
scanf("%lf%lf",&a[i].x,&a[i].y);
}
for(int i = 0; i < (1 << n); i++)
{
dp[i] = 0;
for(int j = 1; j <= n; j++)
{
if(i >> (j - 1) & 1)
{
++dp[i];
}
}
// printf("dp[%lld] = %lld
",i,dp[i]);
}
for(int S = 0; S < (1 << n); S++)
{
int s = 0;
for(int i = 1; i <= n; i++)
{
for(int j = i + 1; j <= n; j++)
{
if(!((S >> (i - 1) & 1) && (S >> (j - 1) & 1))) continue;
if(fabs(a[i].x - a[j].x) < eps) continue;
double A,B;
equ(A,B,a[i].x,a[j].x,a[i].y,a[j].y);
if(A > -eps) continue;
s = (1 << (i - 1)) + (1 << (j - 1));
for(int k = 1; k <= n; k++)
{
if(k == i || k == j) continue;
if(fabs(A * a[k].x * a[k].x + B * a[k].x - a[k].y) < eps)
{
// printf("i = %lld,j = %lld, k = %lld
",i,j,k);
s = (s + (1 << (k - 1)));
}
}
if(S - s >= 0) dp[S] = min(dp[S],dp[S - s] + 1);
}
}
}
printf("%lld
",dp[(1 << n) - 1]);
}
return 0;
}