【题解】NOIP-2016 愤怒的小鸟

题目

洛谷

思路

由我弱弱的数学知识可知,三点确定一抛物线,所以尽量让一只鸟经过俩猪(还有一个确定抛物线的点在原点)(除非求出的抛物线 a > 0 a>0 a>0),枚举每两头猪,求出经过这俩猪和原点的抛物线,并看看是否有其他猪在这条抛物线上,最后得到一堆抛物线(当然要包括只经过一头猪的),用这些抛物线经过的猪做状压dp(状压dp部分应该是这题最简单的部分了,不会的看下面的方程)

附上dp方程 f [ S ∣ v [ i ] ] = m i n ( f [ S ∣ v [ i ] ] , f [ S ] + 1 ) ; f[S|v[i]]=min(f[S|v[i]],f[S]+1); f[Sv[i]]=min(f[Sv[i]],f[S]+1);

别说不会求抛物线

由于 y [ i ] = a x [ i ] 2 + b [ i ] 且 y [ j ] = a x [ j ] 2 + b x [ j ] y[i]=ax[i]^2+b[i] 且 y[j]=ax[j]^2+bx[j] y[i]=ax[i]2+b[i]y[j]=ax[j]2+bx[j]加减消元(初中生必备技能),可得 a = x [ j ] y [ i ] − x [ i ] y [ j ] x [ i ] x [ j ] ( x [ i ] − x [ j ] ) a={x[j]y[i]-x[i]y[j] \over x[i]x[j](x[i]-x[j])} a=x[i]x[j](x[i]x[j])x[j]y[i]x[i]y[j] b = x [ j ] 2 y [ i ] − x [ i ] 2 y [ j ] x [ i ] x [ j ] ( x [ j ] − x [ i ] ) b={x[j]^2y[i]-x[i]^2y[j] \over x[i]x[j](x[j]-x[i])} b=x[i]x[j](x[j]x[i])x[j]2y[i]x[i]2y[j]

代码

#include<bits/stdc++.h>
using namespace std;
#define cl(x) memset(x,0,sizeof(x))
#define inf 2222
typedef double dd;

const int maxn=20;
int n,m;
dd x[maxn],y[maxn];
bool bo[maxn][maxn];
int s[maxn][maxn];
int f[(1<<20)];

void init();
int p=0;
int v[maxn*maxn];
void pre(){
    dd a,b;p=0;
    cl(s);cl(bo);
    for(int i=1;i<n;i++)
        for(int j=i+1;j<=n;j++)if(!bo[i][j]){
            a=(y[i]*x[j]-y[j]*x[i])/(x[i]*x[j]*(x[i]-x[j]));
            b=(y[i]*x[j]*x[j]-y[j]*x[i]*x[i])/(x[i]*x[j]*(x[j]-x[i]));
            if(a>=0){bo[i][j]=1;continue;}
            s[i][j]|=1<<(i-1);
            s[i][j]|=1<<(j-1);
            for(int k=j+1;k<=n;k++)
                if(y[k]==a*x[k]*x[k]+b*x[k])
                    s[i][j]|=1<<(k-1),bo[j][k]=1;
            v[++p]=s[i][j];
        }
    for(int i=1;i<=n;i++)v[++p]=1<<(i-1);
    return ;
}

void dp(){
    for(int i=0;i<(1<<n);i++)f[i]=inf;
    f[0]=0;
    for(int i=1;i<=p;i++)
            for(int S=0;S<(1<<n);S++)
                f[S|v[i]]=min(f[S|v[i]],f[S]+1);
    printf("%d\n",f[(1<<n)-1]);
    return ;
}

int main(){
//    freopen("in","r",stdin);freopen("1.out","w",stdout);
    int T;
    scanf("%d",&T);
    while(T--){
        init();
        pre();
        dp();
    }
    return 0;
}

void init(){
    cl(bo);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%lf%lf",&x[i],&y[i]);
    return ;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值