「Luogu」2831愤怒的小鸟

题意:原题在这

有一架弹弓位于 (0,0)(0,0) 处,每次 Kiana 可以用它向第一象限发射一只红色的小鸟,小鸟们的飞行轨迹均为形如 y=ax^2+bxy=ax2+bx 的曲线,其中 a,ba,b 是Kiana 指定的参数,且必须满足 a < 0a<0,a,ba,b 都是实数。

当小鸟落回地面(即 xx 轴)时,它就会瞬间消失。

在游戏的某个关卡里,平面的第一象限中有 nn 只绿色的小猪,其中第 ii 只小猪所在的坐标为 \left(x_i,y_i \right)(xi,yi)。

如果某只小鸟的飞行轨迹经过了 \left( x_i, y_i \right)(xi,yi),那么第 ii 只小猪就会被消灭掉,同时小鸟将会沿着原先的轨迹继续飞行;

如果一只小鸟的飞行轨迹没有经过 \left( x_i, y_i \right)(xi,yi),那么这只小鸟飞行的全过程就不会对第 ii 只小猪产生任何影响。

例如,若两只小猪分别位于 (1,3)(1,3) 和 (3,3)(3,3),Kiana 可以选择发射一只飞行轨迹为 y=-x^2+4xy=x2+4x 的小鸟,这样两只小猪就会被这只小鸟一起消灭。

而这个游戏的目的,就是通过发射小鸟消灭所有的小猪。

这款神奇游戏的每个关卡对 Kiana来说都很难,所以Kiana还输入了一些神秘的指令,使得自己能更轻松地完成这个游戏。这些指令将在【输入格式】中详述。

假设这款游戏一共有 TT 个关卡,现在 Kiana想知道,对于每一个关卡,至少需要发射多少只小鸟才能消灭所有的小猪。由于她不会算,所以希望由你告诉她。

思路:

1. 先枚举每一对瓷猪解出抛物线参数A,B,再在精度范围内更新shoot状态

2. shoot[20][20]表示经过i,j的抛物线能达到的瓷猪数量(状态)

3. 对于一条抛物线上的多个瓷猪:dp[i|shoot[j][k]]=min(dp[i|shoot[j][k]],dp[i]+1);

对于某些单独的瓷猪:dp[i|(1<<(j-1))]=min(dp[i|(1<<(j-1))],dp[i]+1);

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#define inf 999999999
using namespace std;
const double eps=1e-7;//精度范围内的浮点数比较

int t,n,m;
double A,B;//抛物线参数
int shoot[20][20];//表示经过i,j的抛物线能达到的瓷猪数量(状态)
double x[20],y[20];
int dp[1<<20];

int main()
{
    scanf("%d",&t);
    for (int f=1;f<=t;f++)
    {
        scanf("%d%d",&n,&m);
        memset(shoot,0,sizeof(shoot));
        for(int i=0;i<(1<<20);i++) dp[i]=inf;
        dp[0]=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%lf%lf",&x[i],&y[i]);
        }
        for (int i=1;i<n;i++)
        for (int j=i+1;j<=n;j++)
        {
            A=(y[i]*x[j]-y[j]*x[i])/(x[i]*x[i]*x[j]-x[j]*x[j]*x[i]);
            if (A>=0) continue;
            B=(y[i]-A*x[i]*x[i])/x[i];
            for (int k=1;k<=n;k++)
            {
                if(abs(A*x[k]*x[k]+B*x[k]-y[k])<=eps) shoot[i][j]+=(1<<(k-1));
            }
        }
        for(int i=0;i<(1<<n);i++)
        {
            if(dp[i]==inf) continue;
            for (int j=1;j<n;j++)
            for (int k=j+1;k<=n;k++)
            {
                dp[i|shoot[j][k]]=min(dp[i|shoot[j][k]],dp[i]+1);
            }
            for (int j=1;j<=n;j++)
            {
                dp[i|(1<<(j-1))]=min(dp[i|(1<<(j-1))],dp[i]+1);
            }
        }
        printf("%d\n",dp[(1<<n)-1]);
    }
}

题解里面也有一种方法很好理解但是写挂了,贴上思路及告瓷代码:

1.定义dp[i][j]表示开了i炮现在的情况是j(状态),的第k位为1表示这头猪已经被干掉。

   dp[i][j]是一个bool,true表示i和j的情况存在。

   那么我们每次转移是枚举每个可能的炮弹,还要特判一下只打一头猪的情况。


2.预处理

   枚举任意两头猪,然后待定系数法解出a,b
   之后枚举每一个点,判定是否可以被打到
   最后输出的数是一个二进制数,表示这一炮可以打到

void formula(int t1,int t2)
{
    double x1=pos[t1].x;double x2=pos[t2].x;
    double y1=pos[t1].y;double y2=pos[t2].y;
    pxa=(x2*y1-x1*y2)/(x1*x2*(x1-x2));
    if(a>-eps) return;
    shoot[++cnt]=digit-1;
    pxb=(y1/x1)-a*x1;
    for(int i=1;i<=n;i++)
    {
        double xx=pos[i].x;double yy=pos[i].y;
        double temp=a*xx*xx+b*xx;
        if(temp-eps<yy&&yy<temp+eps)
        {
            shoot[cnt]|=(1<<i-1);//这头小猪凉凉
        }
        if(vis[shoot[cnt]]==1) cnt--;
        else vis[shoot[cnt]]=1;
        return;
    }
}


void fuck()
{
    for(int i=1;i<=t;i++)
    {
        scanf("%d%d",&n,&m);
        digit=1<<n;
        for(int j=1;j<=n;j++)
        {
            scanf("%lf%lf",&pos[j].x,&pos[j].y);
        }
    }
    cnt=0;
    memset(vis,0,sizeof(vis));
    for(int t1=1;t1<=n;t1++)
    for(int t2=1;t2<t1;t2++)
    {
        formula(t1,t2);
    }
    memset(dp,0,sizeof(dp));
    dp[0][digit-1]=1;
    for(int i=1;i<=n;i++)
    for(int j=0;j<digit;j++)
    {
        if(dp[i][j]==1)//如果存在
        {
            for(int t1=1;t1<=n;t1++)
            {
                int q=(j|(1<<t1));//特判只打一头的情况
                if(q==0)//第一个合法一定是最小
                {
                    printf("%d\n",i+1);
                    return;
                }
                dp[i+1][q]=1;
            }
            for(int k=1;k<=cnt;k++)//枚举抛物线
            {
                int p=shoot[k]&j;
                if(p==0)
                {
                    printf("%d\n",i+1);
                    return;
                }
                if(p!=j)
                {
                    dp[i+1][p]=1;
                }
            }
        }
    }
    return;
}

猜你喜欢

转载自www.cnblogs.com/LocaEtric/p/9614589.html
今日推荐