【NOIP2016提高组day2】愤怒的小鸟

8 篇文章 1 订阅
2 篇文章 0 订阅

分析

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

分析

首先先求出每个点两两之间的函数解析式,并且求出这个解析式还能消灭那只猪。
状压背包,
fs 表示状态为s的最小值(其中s为二进制数,表示每只猪有没有被消灭)
转移方程很简单 fs|s=min(fs|s,fs+1)
时间复杂度 O(2nn2)

对了,注意精度

#include <cmath>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
const int maxlongint=2147483647;
const int mo=1000000007;
const int N=550005;
using namespace std;
int f[N],b[20][20],n,m,t,d[500],mi[N];
double a[N][2],a1,b1;
int min1(int x,int y)
{
    return x>y?y:x;
}
int main()
{
    freopen("angrybirds.in","r",stdin);
    freopen("angrybirds.out","w",stdout);
    mi[0]=1;
    for(int i=1;i<=20;i++)
    {
        mi[i]=mi[i-1]*2;
    }
    scanf("%d",&t);
    for(;t--;)
    {
        scanf("%d%d",&n,&m);
        memset(b,0,sizeof(b));
        memset(f,43,sizeof(f));
        f[0]=0;
        for(int i=1;i<=n;i++)
            scanf("%lf%lf",&a[i][0],&a[i][1]);
        for(int i=1;i<=n;i++)
        {
            b[i][i]=mi[i-1];
            for(int j=1;j<=n;j++)
            if(i!=j)
            {
                double y=a[i][1],y1=a[j][1],x=a[i][0],x1=a[j][0],xx=a[i][0]*a[i][0],xx1=a[j][0]*a[j][0];
                a1=b1=0;
                y*=xx1;
                x*=xx1;
                y1*=xx;
                x1*=xx;
                y-=y1;
                x-=x1;
                b1=y/x;
                a1=(a[i][1]-a[i][0]*b1)/(a[i][0]*a[i][0]);
                if(a1>=0) continue;
                b[i][j]=mi[i-1]+mi[j-1];
                for(int k=1;k<=n;k++)
                if(k!=i && k!=j)
                {
                    if(a[k][1]-a[k][0]*a[k][0]*a1-a[k][0]*b1<=0.000000000001 && a[k][1]-a[k][0]*a[k][0]*a1-a[k][0]*b1>=-0.000000000001)
                    {
                        b[i][j]+=mi[k-1];
                    }
                }
            }
        }
        d[0]=0;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            {
                bool q=true;
                if(f[b[i][j]]==1 || b[i][j]==0) continue;
                for(int k=1;k<=n && q;k++)
                    for(int l=1;l<=n && q;l++)
                        if((b[i][j]&b[k][l])>=b[i][j] && b[i][j]!=b[k][l]) q=false;
                if(q) d[++d[0]]=b[i][j],f[b[i][j]]=1;
            }
        for(int i=1;i<=d[0];i++)
            for(int s=0;s<=mi[n]-1;s++)
            {
                f[s|d[i]]=min1(f[s|d[i]],f[s]+1);
            }
        printf("%d\n",f[mi[n]-1]);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值