zoukankan      html  css  js  c++  java
  • 「Luogu」2831愤怒的小鸟 (DFS+dp)

    题意:原题在这

    有一架弹弓位于 (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 ight)(xi,yi)。

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

    如果一只小鸟的飞行轨迹没有经过 left( x_i, y_i ight)(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
    ",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
    ",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
    ",i+1);
                        return;
                    }
                    if(p!=j)
                    {
                        dp[i+1][p]=1;
                    }
                }
            }
        }
        return;
    }
  • 相关阅读:
    uboot向内核模块传递参数的方法
    arm下用shell控制gpio
    u-boot的内存分布和全局数据结构
    Ambarella SDK build 步骤解析
    MMU段式映射(VA -> PA)过程分析
    ambarella H2 添加文件到ext4文件系统
    使用U-Boot的TFTP(远程/网络内核)
    使用U-Boot的NFS(远程/网络用户空间)
    君正Ingenic X1000E_halley2 更改Logo
    【自动化测试】robotframework中一些建议可能需要掌握的关键字
  • 原文地址:https://www.cnblogs.com/LocaEtric/p/9614589.html
Copyright © 2011-2022 走看看