NOIP2016 D2-T3 愤怒的小鸟

看了题解之后知道,是状压dp。

一、首先预处理一个$2^n$次方的fpow[]数组

fpow[0]=1;
for(Rint i=1;i<=19;i++)fpow[i]=(fpow[i-1]<<1);

二、然后预处理一个sta[i][j],表示经过O,i,j三点的那条抛物线经过的所有的点的状态,处理时要注意:

1、抛物线的$a$值大于等于0的要剔除

2、横坐标相同的两点不可能在同一条抛物线上

3、注意精度
处理完后就可以状压dp了。

三、dp时首先把dp[]值赋为inf

方程:

chkmin(dp[i|sta[J][K]],dp[i]+1)

四、最后的优化:

1、就是我们没有必要枚举所有的$i$,其实不论如何,在$dp[i]$中第一个没有出现的猪,我们最后一定要打的,所以我们干脆就只枚举最后那个猪,这样可以快一点

2、找到第一只没打过的猪后,只需要枚举从它开始剩下的猪即可,不要从1开始:

for(Rint k=j+1;k<=n;k++){//即此循环只需从j+1开始,而不需从1枚举到n
  int J=min(j,k),K=max(j,k);
  chkmin(dp[i|sta[J][K]],dp[i]+1);
}

 五、Attention!(洛谷AC,UOJ WA)

假如得了97分的话,估计是精度问题

判断a的正负时要if(a<-1e-6)

判断是否是同一解要if(Abs(...)<=1e-12)

否则会炸extra test

 

六、最后上AC代码:

#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#define Rint register int
#define mem(a,b) memset(a,(b),sizeof(a))
using namespace std;
template<typename T>
inline void read(T &x){
    x=0;T w=1,ch=getchar();
    while(!isdigit(ch)&&ch!='-')ch=getchar();
    if(ch=='-')w=-1,ch=getchar();
    while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar();
    x=x*w;
}
template<typename T>
inline T Min(T &x,T &y){return x<y?x:y;}
template<typename T>
inline void chkmin(T &x,T y){if(y<x)x=y;}
inline double Abs(double x){return x<0?-x:x;}

const int maxn=23,inf=0x3f3f3f3f;
const double eps=1e-12,eeps=-1e-6;
int fpow[21];
int n,m,sta[maxn][maxn],dp[1<<maxn];
double x[maxn],y[maxn];
inline void init(){
    n=0;
    m=0;
}
inline void update_sta(int a,int b){
    sta[a][b]=0;
    double x1=x[a],y1=y[a],x2=x[b],y2=y[b];
    if(x1==x2)return;
    double A=(y1*x2-y2*x1)/(x1*x1*x2-x2*x2*x1);
    if(A>=eeps)return;
    double B=(y1-A*x1*x1)/x1;
    for(Rint i=1;i<=n;i++)
        if(Abs(A*x[i]*x[i]+B*x[i]-y[i])<eps)
            sta[a][b]|=fpow[i-1];
}
int main(){
    fpow[0]=1;
    for(Rint i=1;i<=19;i++)fpow[i]=(fpow[i-1]<<1);
    int TT;
    read(TT);
    while(TT--){
        init();
        read(n);read(m);
        for(Rint i=1;i<=n;i++)scanf("%lf%lf",&x[i],&y[i]);
        for(Rint i=1;i<=n;i++)
            for(Rint j=i+1;j<=n;j++)
                update_sta(i,j);
        for(Rint i=0;i<fpow[n];i++)dp[i]=inf;
        dp[0]=0;
        for(Rint i=0;i<fpow[n];i++){
            if(dp[i]==inf)continue;
            for(Rint j=1;j<=n;j++){
                if(!(i&fpow[j-1])){
                    for(Rint k=j+1;k<=n;k++){
                        int J=min(j,k),K=max(j,k);
                        chkmin(dp[i|sta[J][K]],dp[i]+1);
                    }
                    chkmin(dp[i|fpow[j-1]],dp[i]+1);
                    break;
                }
            }
        }
        printf("%d\n",dp[fpow[n]-1]);
    }
    return 0;
}
AC代码

 

posted @ 2017-10-29 10:30  ChinHhh  阅读(317)  评论(0编辑  收藏  举报