「NOIP2016」愤怒的小鸟

「NOIP2016」愤怒的小鸟


Description

\(Kiana\) 最近沉迷于一款神奇的游戏无法自拔。
简单来说,这款游戏是在一个平面上进行的。有一架弹弓位于 \((0,0)\) 处,每次 \(Kiana\) 可以用它向第一象限发射一只小鸟,小鸟们的飞行轨迹均为形如\(y=ax^2+bx\)的曲线,其中 \(a\)\(b\)\(Kiana\) 指定的参数,且必须满足 \(a<0\)。当小鸟落回地面(即 \(x\) 轴)时,它就会瞬间消失。
在游戏的某个关卡里,平面的第一象限中有 \(n\) 只猪,其中第 \(i\) 只猪所在的坐标为 \((x_i,y_i)\)。如果某只小鸟的飞行轨迹经过了\((x_i,y_i)\),那么第 \(i\)只猪就会被消灭掉,同时小鸟将会沿着原先的轨迹继续飞行;如果一只小鸟的飞行轨迹没有经过\((x_i,y_i)\),那么这只小鸟飞行的全过程就不会对第\(i\)只猪产生任何影响。
例如,若两只猪分别位于\((1,3)\)\((3,3)\)\(Kiana\) 可以选择发射一只飞行轨迹为 \(y=−x^2+4x\) 的小鸟,这样两只猪就会被这只小鸟一起消灭。
而这个游戏的目的,就是通过发射小鸟消灭所有的猪。
这款神奇游戏的每个关卡对来说都很难,所以 \(Kiana\)还输入了一些神秘的指令,使得自己能更轻松地完成这个游戏。这些指令将在「输入格式」中详述。
假设这款游戏一共有 \(T\) 个关卡,现在\(Kiana\)想知道,对于每一个关卡,至少需要发射多少只小鸟才能消灭所有的猪。由于她不会算,所以希望由你告诉她。


Input Format

下面依次输入这 T 个关卡的信息。每个关卡第一行包含两个非负整数 \(n\)\(m\) ,分别表示该关卡中的小猪数量和 \(Kiana\) 输入的神秘指令类型。接下来的 \(n\) 行中,第 \(i\) 行包含两个正实数 \(x_i\)\(y_i\),表示第 \(i\) 只小猪坐标为 \((x_i,y_i)\)。数据保证同一个关卡中不存在两只坐标完全相同的小猪。

如果 \(m=0\),表示 \(Kiana\) 输入了一个没有任何作用的指令。
如果 \(m=1\) ,则这个关卡将会满足:至多用\(\lceil n/3+1 \rceil\)只小鸟即可消灭所有小猪。
如果 \(m=2\) ,则这个关卡将会满足:一定存在一种最优解,其中有一只小鸟消灭了至少 $\lfloor n/3 \rfloor $ 只小猪。
保证 \(1≤n≤18\)\(0≤m≤2\)\(0<x_i,y_i<10\),输入中的实数均保留到小数点后两位。
上文中,符号 分别表示对 c 向上取整和向下取整,例如:
\(\lceil 2.1 \rceil = \lceil 2.9 \rceil = \lceil 3.0 \rceil = \lfloor 3.0 \rfloor = \lfloor 3.1 \rfloor = \lfloor 3.9 \rfloor = 3\)


Output Format

对每个关卡依次输出一行答案。
输出的每一行包含一个正整数,表示相应的关卡中,消灭所有猪最少需要的小鸟数量。

simple 1

输入

2
2 0
1.00 3.00
3.00 3.00
5 2
1.00 5.00
2.00 8.00
3.00 9.00
4.00 8.00
5.00 5.00

输出

1
1

simple 2

输入

3
2 0
1.41 2.00
1.73 3.00
3 0
1.11 1.41
2.34 1.79
2.98 1.49
5 0
2.72 2.72
2.72 3.14
3.14 2.72
3.14 3.14
5.00 5.00

输出

2
2
3

simple 3

输入

1
10 0
7.16 6.28
2.02 0.38
8.33 7.78
7.68 2.09
7.46 7.86
5.77 7.44
8.24 6.72
4.42 5.11
5.42 7.79
8.15 4.99

输出

6

Hint

样例解释 1

这组数据中一共有两个关卡。

第一个关卡与「问题描述」中的情形相同,\(2\) 只猪分别位于 \((1.00,3.00)\)\((3.00,3.00)\),只需发射一只飞行轨迹为 \(y=−x^2+4x\) 的小鸟即可消灭它们。

第二个关卡中有 \(5\) 只猪,但经过观察我们可以发现它们的坐标都在抛物线 \(y=−x^2+6x\) 上,故 \(Kiana\) 只需要发射一只小鸟即可消灭所有猪。

数据范围

测试点 \(1∼14:2≤n≤12,1≤T≤30\)

测试点 \(15∼20:2≤n≤18,1≤T≤5\)


Solution

首先我们 \(n≤18\) 一看就知道是搜索或者是状压

但是这题我们考虑用状压dp来写

首先根据状压的尿性,我们首先要设计一个状态

我们用\(a[i][j]\)来表示一只能打到第\(i\)只猪和第\(j\)只猪的鸟能打到多少只猪

所以每次我们枚举出这一只鸟以后我们可以再用一个\(k\) 循环来判断还有哪些猪能被这只鸟打到

如果第\(k\)只猪也能被这只鸟打到,那么\(a[i][j]|=(1<<k)\)(第几位是\(1\)就表示第几只猪能被这只鸟打到)

但是,问题来了,我们应该如何找出这只鸟呢?

首先,就像题目说的每只鸟说经过的弧线都可以表示成\(y=ax^2+bx\)

所以我们可以根据题目,列出下面的方程
在看下面的解释之前,请你先会逆矩阵求法,矩阵乘法等数学基础

$ y_1=ax_1^2+bx_1$
\(y_2=ax_2^2+bx_2\)

然后我们把它变成矩阵,就有

$ \left[
\begin{matrix}
y_1\
y_2\
\end{matrix}
\right]
$ \(=\) \(\left[ \begin{matrix} x_1^2& x1\\ x_2^2&x2\\ \end{matrix} \right]\) \(×\) \(\left[ \begin{matrix} a\\ b\\ \end{matrix} \right]\)

然后就有

$ \left[
\begin{matrix}
a\
b\
\end{matrix}
\right]
$ \(=\) \(\left[ \begin{matrix} x_1^2 & x_1\\ x_2^2 & x_2\\ \end{matrix} \right]^{-1}\) \(×\) \(\left[ \begin{matrix} y_1\\ y_2\\ \end{matrix} \right]\)

因为

\(\left[ \begin{matrix} x_1^2&x_1\\ x_2^2&x_2\\ \end{matrix} \right]\) \(=\) \(x_1^2x_2-x_1x_2^2=(x_1-x_2)x_1x_2\)

所以有:
\(\left[ \begin{matrix} x_1^2&x_1\\ x_2^2&x_2\\ \end{matrix} \right]^{-1}\) \(=\) \(\frac{1}{(x_1-x_2)x_1x_2}\) $\left[
\begin{matrix}
x_2& -x_1\
-x_2^2&x_1^2\
\end{matrix}
\right]
$

(逆矩阵求法)

于是:
$ \left[
\begin{matrix}
a\
b\
\end{matrix}
\right]
$ \(=\) \(\frac{1}{(x_1-x_2)x_1x_2}\) $
\left[
\begin{matrix}
x_2&-x_1\
-x_2^2&x_1^2\
\end{matrix}
\right]
$ \(×\) $\left[
\begin{matrix}
y_1\
y_2\
\end{matrix}
\right]
$

最后,我们再把矩阵拆开:

\(a=\frac{1}{(x_1-x_2)x_1x_2}×(x_2y_1-x_1y_2)\)
\(b=\frac{1}{(x_1-x_2)x_1x_2}×(x_1^2y_2-x_2^2y_1)\)

最后我们就求出了\(a\)\(b\)
因为\(x_1\) \(x_2\) \(y_1\) \(y_2\)可以通过枚举知道
然后我们枚举所有状态,再用所有的抛物线去枚举这个状态,假设这个状态为\(i\),这个抛物线能消灭的猪是\(a[Start][j]\),其中\(Start\)开始的位置我们可以用一个\(while\)语句来查找,那么用这个抛物线消灭之后变成的状态就是\(a[Start][j]\),其中\(|\)是按位或运算。dp方程为\(f[i|a[Start][j]]=min(f[i|a[Start][j]],f[i]+1)\)

最后输出答案\(f[(1<<n)-1]\)
所以本题就结束了......


代码

#include<bits/stdc++.h>
#define Re register int
using namespace std;
int N,M,T,a[20][20],f[1<<20],Start;
double A,B;
struct info{
    double x,y;
}Pig[20];
double fabs(double x){return x>0?x:-x;}
int main(){
    scanf("%d",&T);
    while (T--){
        memset(f,0x3f,sizeof(f));
        memset(a,0,sizeof(a));
        scanf("%d%d",&N,&M);
        for (Re i=0; i<N; ++i) scanf("%lf%lf",&Pig[i].x,&Pig[i].y);
        for (Re i=0; i<N; ++i) a[i][i]|=(1<<i);
        for (Re i=0; i<N; ++i)
        for (Re j=i+1; j<N; ++j)
            if (fabs(Pig[i].x-Pig[j].x)>1e-6){
                A=(Pig[j].x*Pig[i].y-Pig[i].x*Pig[j].y)/((Pig[i].x-Pig[j].x)*Pig[i].x*Pig[j].x);
                B=(Pig[i].y-A*Pig[i].x*Pig[i].x)/Pig[i].x;
                if (A>-1e-6) continue;
                for (Re k=0; k<N; ++k) if (fabs(A*Pig[k].x*Pig[k].x+B*Pig[k].x-Pig[k].y)<=1e-6) a[i][j]|=(1<<k); 
            }
        f[0]=0;
        for (Re i=0; i<1<<N; ++i){
            Start=0;
            while ((i>>Start)&1) ++Start;
            for (Re j=Start; j<N; ++j) f[i|a[Start][j]]=min(f[i|a[Start][j]],f[i]+1);
        }
        printf("%d\n",f[(1<<N)-1]);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/to-the-end/p/11331817.html
今日推荐