NOIP系列(续)

Posted on 2017-10-26 21:27  Amphetamine  阅读(207)  评论(0编辑  收藏  举报

马上就要告别noip了呢。

 

这道题大家都说dfs可过。

但是数据范围一眼状压啊。

首先假设点是有序的(选取有先后顺序),其实这并不影响什么,但是却省下大量的时间和代码长度。

然后状压,dp[i]表示状态为i最少需要再用几条抛物线。

注意在枚举每一个点的抛物线的时候考虑在这个抛物线上会不会有其它点与它重合。

#include<bits/stdc++.h>
using namespace std;
#define eps 1e-10
inline double abs1(double a){return max(a, -a);}
int dp[(1<<18)];
double x[19],y[19];
int n;
double a,b;
void calc(int i,int j){
    if(x[i]==x[j]){
        a=1;return ;
    }
    b=(y[i]*x[j]*x[j]-y[j]*x[i]*x[i])/(x[i]*x[j]*(x[j]-x[i]));
    a=(y[i]-x[i]*b)/(x[i]*x[i]); 
}
 
int dfs(int i,int st){
    if(dp[st])return dp[st];
    if(!st)return 0;
    if(!((1<<(i-1))&st))return dp[st]=dfs(i+1,st);
    int ret=1+dfs(i+1,st^(1<<(i-1)));//自己一条抛物线
    int sta=st;
    for(int j=i+1;j<=n;j++){//两点确定一条抛物线,所以只要枚举下一个点就害了
        if((1<<(j-1))&sta){
        sta|=(1<<(j-1));
        calc(i,j);
        if(a>-eps)continue;//判断能否在同一条抛物线上
        int tmp=st^(1<<(j-1))^(1<<(i-1));
        for(int k=i+1;k<=n;k++){
            if(!(tmp&(1<<(k-1))))continue;
            double yy=a*x[k]*x[k]+b*x[k];
            if(abs1(yy-y[k])<eps){
            tmp^=(1<<(k-1));sta|=(1<<(k-1));}
        }
        ret=min(ret,1+dfs(i+1,tmp));
        }
    }
    return dp[st]=ret;
}
int T;
int main(){
    scanf("%d",&T);
    while(T--){
    memset(dp,0,sizeof(dp));
    int m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)dp[1<<(i-1)]=1;
    for(int i=1;i<=n;i++)scanf("%lf%lf",&x[i],&y[i]);
    dfs(1,(1<<n)-1);
    printf("%d\n",dp[(1<<n)-1]);
    }
    return 0;
}
View Code