luogu2831 [NOIp2016]愤怒的小鸟 (状压dp)

时间:2023-06-30 18:17:07

由范围可以想到状压dp

两个点(再加上原点)是可以确定一个抛物线的,除非它们解出来a>=0,在本题中是不合法的

这样的话,我们可以预处理出由任意两个点确定的抛物线所经过的所有的点(要特别规定一下自己和自己确定的抛物线只经过自己)

然后设状态s表示目前已经有哪些点被击中了,然后我们钦定这次就要打那个最小的还没击中的点(因为吃枣都要打的嘛),再枚举出另一个还没经过的点,就能得到转移方程

$f[s|line[i][j]]=max\{f[s]+1\}$,其中$line[i][j]$表示i、j两点确定的抛物线经过的所有的点,i是s中还未击中的最小的点

复杂度是$O(n2^n)$

 #include<bits/stdc++.h>
#define pa pair<int,int>
#define lowb(x) ((x)&(-(x)))
#define REP(i,n0,n) for(i=n0;i<=n;i++)
#define PER(i,n0,n) for(i=n;i>=n0;i--)
#define MAX(a,b) ((a>b)?a:b)
#define MIN(a,b) ((a<b)?a:b)
#define CLR(a,x) memset(a,x,sizeof(a))
#define rei register int
using namespace std;
typedef long long ll;
const int maxn=,maxs=; inline ll rd(){
ll x=;char c=getchar();int neg=;
while(c<''||c>''){if(c=='-') neg=-;c=getchar();}
while(c>=''&&c<='') x=x*+c-'',c=getchar();
return x*neg;
} int N;
double pos[maxn][];
int line[maxn][maxn];
int f[maxs],bin[maxn]; inline bool eq(double a,double b){return fabs(a-b)<=1e-;}
inline void getab(double &a,double &b,double x1,double y1,double x2,double y2){
a=(x2*y1-x1*y2)/(x1*x2*(x1-x2));
b=(x1*x1*y2-x2*x2*y1)/(x1*x2*(x1-x2));
} int main(){
// freopen("testdata.in","r",stdin);
int i,j,k;
for(i=,j=;i<=;i++,j<<=) bin[i]=j;
for(int T=rd();T;T--){
N=rd();rd();
for(i=;i<=N;i++) scanf("%lf%lf",&pos[i][],&pos[i][]);
for(i=;i<=N;i++){
for(j=i+;j<=N;j++){
double a,b;getab(a,b,pos[i][],pos[i][],pos[j][],pos[j][]); int s=;
if(a<){
for(k=;k<=N;k++ ){
if(eq(a*pos[k][]*pos[k][]+b*pos[k][],pos[k][])){
s|=bin[k];
}
}
}
line[i][j]=s;
// cout<<a<<" "<<b<<" "<<i<<" "<<j<<" "<<bitset<20>(s)<<endl;
}line[i][i]=bin[i];
}
memset(f,,sizeof(f));
f[]=;
for(i=;i<bin[N+]-;i++){
for(j=;bin[j]&i;j++);
for(k=j;k<=N;k++){
if(bin[k]&i) continue;
f[i|line[j][k]]=min(f[i|line[j][k]],f[i]+);
}
}printf("%d\n",f[bin[N+]-]);
} return ;
}