【NOIP2016提高组】 Day2 T3 愤怒的小鸟
题目传送门:https://www.luogu.org/problemnew/show/P2831
说个题外话:NOIP2014也有一道题叫做愤怒的小鸟。
这题自测时算错了eps,导致被卡了精度,从100卡剩80
由于此题n的范围特别小,所以考虑使用状压dp。
我们用一个整数i来描述状态,i的第k个bit表示第k只鸟是否被消灭,f[i]表示在这一状态下所需鸟的最小数量。
由于不在同一直线上的三个点确定一条抛物线,所以我们可以预处理出所有的可行抛物线,并确定在该抛物线上的鸟的编号(这一步对精度要求极高)。同时考虑到两只鸟与原点存在三点共线的情况,故在抛物线方案中写入单独消灭指定一只小鸟的情况。故消灭方案之多有(n*(n-1)/2+n)种。
然后就愉快地进行转移啦~ 顺便吐槽下m没有卵用
时间复杂度为O(n^2*2^n)。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #define M 18 6 #define eps (1e-10) 7 using namespace std; 8 int f[1<<M]={0}; 9 int n; 10 struct pt{ 11 int x,y; pt(){x=y=0;} 12 pt(double xx,double yy){x=xx; y=yy;} 13 }a[M]; 14 struct pao{ 15 double a,b; pao(){a=b=0;} 16 pao(pt A,pt B){ 17 double x0=A.x,x1=B.x,y0=A.y,y1=B.y; 18 a=(y1-y0*x1/x0)/(x1*x1-x0*x1); 19 b=y0/x0-a*x0; 20 } 21 bool check(){ 22 if(a>-eps) return 0; 23 return 1; 24 } 25 double get(double x){ 26 double ans=a*x*x+b*x; 27 return ans; 28 } 29 }; 30 int fly[M*M]={0},use=0; 31 bool b[1<<M]={0}; 32 int Main(){ 33 memset(f,1,sizeof(f)); 34 memset(a,0,sizeof(a)); 35 memset(fly,0,sizeof(fly)); 36 memset(b,0,sizeof(b)); 37 use=0; int m; 38 scanf("%d%d",&n,&m); 39 for(int i=1;i<=n;i++){ 40 double x,y; scanf("%lf%lf",&x,&y); 41 x=x*100+0.3; y=y*100+0.3; 42 a[i]=pt(x,y); 43 } 44 for(int i=0;i<n;i++) fly[++use]=1<<i; 45 for(int i=1;i<=n;i++){ 46 for(int j=1;j<i;j++) if(i!=j){ 47 if(a[i].x==a[j].x) continue; 48 pao A=pao(a[i],a[j]); 49 if(!A.check()) continue; 50 int x=0; 51 for(int k=0;k<n;k++){ 52 double p=A.get(a[k+1].x); 53 if(fabs(p-a[k+1].y)<=eps) 54 x=x|(1<<k); 55 } 56 if(!b[x]) 57 fly[++use]=x,b[x]=1; 58 } 59 } 60 int end=1<<n; f[0]=0; 61 for(int i=0;i<end;i++){ 62 for(int j=1;j<=use;j++) 63 f[i|fly[j]]=min(f[i|fly[j]],f[i]+1); 64 } 65 printf("%d\n",f[end-1]); 66 } 67 68 int main(){ 69 freopen("angrybirds.in","r",stdin); 70 freopen("angrybirds.out","w",stdout); 71 int cas; scanf("%d",&cas); 72 while(cas--) Main(); 73 return 0; 74 }