UOJ265 NOIP2016 day2 T3 愤怒的小鸟(状压DP+预处理)

51 篇文章 1 订阅
9 篇文章 0 订阅

UOJ265 NOIP2016 day2 T3 愤怒的小鸟

原题地址http://uoj.ac/problem/265

题意:
有一架弹弓位于 (0,0)(0,0) 处,每次 Kiana 可以用它向第一象限发射一只红色的小鸟,小鸟们的飞行轨迹均为形如 y=ax2+bxy=ax2+bx 的曲线,其中 a,ba,b 是 Kiana 指定的参数,且必须满足 a<0a<0,a,ba,b 都是实数。
当小鸟落回地面(即 xx 轴)时,它就会瞬间消失。
在游戏的某个关卡里,平面的第一象限中有 nn 只绿色的小猪,其中第 ii 只小猪所在的坐标为 (xi,yi)(xi,yi)。
如果某只小鸟的飞行轨迹经过了 (xi,yi)(xi,yi),那么第 ii 只小猪就会被消灭掉,同时小鸟将会沿着原先的轨迹继续飞行;
如果一只小鸟的飞行轨迹没有经过 (xi,yi)(xi,yi),那么这只小鸟飞行的全过程就不会对第 ii 只小猪产生任何影响。
例如,若两只小猪分别位于 (1,3)(1,3) 和 (3,3)(3,3),Kiana 可以选择发射一只飞行轨迹为 y=−x2+4xy=−x2+4x 的小鸟,这样两只小猪就会被这只小鸟一起消灭。
而这个游戏的目的,就是通过发射小鸟消灭所有的小猪。

一共有T组数据,对于每组数据,至少需要发射多少只小鸟才能消灭所有的小猪。

数据范围
保证 1≤n≤18,0≤ m≤2,0< xi,yi< 10,输入中的实数均保留到小数点后两位。
题解:
n^3预处理每两只猪所在抛物线可以射到的猪
枚举猪i,j f[s]=min(f[s|t[i][j]])+1
比较懒,写的记忆化搜索。
代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=20;
const double EPS=1e-10;
int T,n,m,t[N][N],f[1<<N]; 
int inf;
double X[N],Y[N];
int sign(double x)
{
    if(fabs(x)<EPS) return 0;
    else return x<0?-1:1; 
}
bool solve(double x1,double y1,double x2,double y2,double &a,double &b)
{
    double d=(x2/x1)*y1-y2;
    a=d/(x1*x2-x2*x2);
    b=(y1-x1*x1*a)/x1;
    if(sign(a)>=0) return 0;
    return 1;
}
int dp(int state)
{

    if(f[state]!=inf) return f[state];
    for(int i=0;i<n;i++)
    for(int j=i+1;j<n;j++)
    {
        if((state|t[i][j])==state||!t[i][j]) continue;
        f[state]=min(f[state],dp(state|t[i][j])+1);
    }
    for(int i=0;i<n;i++)
    {
        if((state|(1<<i))==state) continue;
        f[state]=min(f[state],dp(state|(1<<i))+1);
    }
    return f[state];
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        memset(t,0,sizeof(t));
        memset(f,60,sizeof(f));
        inf=f[0];
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++)
        {
            scanf("%lf%lf",&X[i],&Y[i]);
        }
        for(int i=0;i<n;i++)
        for(int j=i+1;j<n;j++)
        {
            double a,b;
            if(solve(X[i],Y[i],X[j],Y[j],a,b))
            {
                t[i][j]=t[i][j]|(1<<i);
                t[i][j]=t[i][j]|(1<<j);
                for(int k=0;k<n;k++)
                {
                    if(k==i||k==j) continue;

                    if(sign(a*X[k]*X[k]+b*X[k]-Y[k])==0)
                    t[i][j]=t[i][j]|(1<<k);                 
                }
            }
        }
        int top=(1<<n)-1;
        f[top]=0;
        printf("%d\n",dp(0));       
    }   
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值