luogu2831 [NOIp2016]愤怒的小鸟 (状压dp)
由范围可以想到状压dp
两个点(再加上原点)是可以确定一个抛物线的,除非它们解出来a>=0,在本题中是不合法的
这样的话,我们可以预处理出由任意两个点确定的抛物线所经过的所有的点(要特别规定一下自己和自己确定的抛物线只经过自己)
然后设状态s表示目前已经有哪些点被击中了,然后我们钦定这次就要打那个最小的还没击中的点(因为吃枣都要打的嘛),再枚举出另一个还没经过的点,就能得到转移方程
$f[s|line[i][j]]=max\{f[s]+1\}$,其中$line[i][j]$表示i、j两点确定的抛物线经过的所有的点,i是s中还未击中的最小的点
复杂度是$O(n2^n)$
1 #include<bits/stdc++.h> 2 #define pa pair<int,int> 3 #define lowb(x) ((x)&(-(x))) 4 #define REP(i,n0,n) for(i=n0;i<=n;i++) 5 #define PER(i,n0,n) for(i=n;i>=n0;i--) 6 #define MAX(a,b) ((a>b)?a:b) 7 #define MIN(a,b) ((a<b)?a:b) 8 #define CLR(a,x) memset(a,x,sizeof(a)) 9 #define rei register int 10 using namespace std; 11 typedef long long ll; 12 const int maxn=20,maxs=1000000; 13 14 inline ll rd(){ 15 ll x=0;char c=getchar();int neg=1; 16 while(c<'0'||c>'9'){if(c=='-') neg=-1;c=getchar();} 17 while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); 18 return x*neg; 19 } 20 21 int N; 22 double pos[maxn][2]; 23 int line[maxn][maxn]; 24 int f[maxs],bin[maxn]; 25 26 inline bool eq(double a,double b){return fabs(a-b)<=1e-8;} 27 inline void getab(double &a,double &b,double x1,double y1,double x2,double y2){ 28 a=(x2*y1-x1*y2)/(x1*x2*(x1-x2)); 29 b=(x1*x1*y2-x2*x2*y1)/(x1*x2*(x1-x2)); 30 } 31 32 int main(){ 33 // freopen("testdata.in","r",stdin); 34 int i,j,k; 35 for(i=1,j=1;i<=19;i++,j<<=1) bin[i]=j; 36 for(int T=rd();T;T--){ 37 N=rd();rd(); 38 for(i=1;i<=N;i++) scanf("%lf%lf",&pos[i][0],&pos[i][1]); 39 for(i=1;i<=N;i++){ 40 for(j=i+1;j<=N;j++){ 41 double a,b;getab(a,b,pos[i][0],pos[i][1],pos[j][0],pos[j][1]); 42 43 int s=0; 44 if(a<0){ 45 for(k=1;k<=N;k++ ){ 46 if(eq(a*pos[k][0]*pos[k][0]+b*pos[k][0],pos[k][1])){ 47 s|=bin[k]; 48 } 49 } 50 } 51 line[i][j]=s; 52 // cout<<a<<" "<<b<<" "<<i<<" "<<j<<" "<<bitset<20>(s)<<endl; 53 }line[i][i]=bin[i]; 54 } 55 memset(f,127,sizeof(f)); 56 f[0]=0; 57 for(i=0;i<bin[N+1]-1;i++){ 58 for(j=1;bin[j]&i;j++); 59 for(k=j;k<=N;k++){ 60 if(bin[k]&i) continue; 61 f[i|line[j][k]]=min(f[i|line[j][k]],f[i]+1); 62 } 63 }printf("%d\n",f[bin[N+1]-1]); 64 } 65 66 return 0; 67 }