P2831 [NOIP2016 提高组] 愤怒的小鸟

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)\),那么得:

\[\begin{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 \neq j \neq 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\n",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\n",i,j,k);
                            s = (s + (1 << (k - 1)));
                        }
                    }
                    if(S - s >= 0) dp[S] = min(dp[S],dp[S - s] + 1);
                    
                }
            }
        }
        printf("%lld\n",dp[(1 << n) - 1]);
    }
    return 0;
}
posted @ 2021-08-10 11:11  luyiming123  阅读(46)  评论(0编辑  收藏  举报