luogu P2831 [NOIP2016 提高组] 愤怒的小鸟
题面传送门
数据这么小显然状压dp
考虑状压\(dp_i\)表示打掉的集合为\(i\)时最少抛物线条数。
预处理\(f_{i,j}\)表示过\(i\)点和\(j\)点的抛物线经过的点的集合。
但是这样的转移是\(O(T2^nn^2)\)的。
考虑怎么优化。
很明显我们的\(dp\)转移是无序的。将其变成有序即先打最小的点则可优化掉一个\(n\)变为\(O(T2^nn)\)
代码实现:
#include<cstdio>
#include<cstring>
#define abs(x) ((x)>0?(x):-(x))
#define min(a,b) ((a)<(b)?(a):(b))
#define eps 1e-6
using namespace std;
int n,m,dp[1000039],g[1000039],t,a,b,f[39][39];
double x[39],y[39],nowx,nowy,x1,x2,y1,y2;
int main(){
// freopen("1.in","r",stdin);
register int i,j,k;
scanf("%d",&t);
for(i=0;i<(1<<18);i++){
for(j=1;j<=18;j++) if(!(i&(1<<j-1))){g[i]=j;break;}
}
while(t--){
scanf("%d%d",&n,&m);memset(dp,0x3f,sizeof(dp));dp[0]=0;
for(i=1;i<=n;i++) scanf("%lf%lf",&x[i],&y[i]),dp[(1<<i-1)]=1;
for(i=1;i<=n;i++){
for(j=1;j<=n;j++){
f[i][j]=0;
x1=x[i];x2=x[j];y1=y[i];y2=y[j];
if(abs(x1-x2)<=eps) continue;
nowx=(y1*x2-y2*x1)/(x1*x1*x2-x2*x2*x1);
nowy=(y1-nowx*x1*x1)/x1;
if(nowx>0) continue;
for(k=1;k<=n;k++)
if(abs(nowx*x[k]*x[k]+nowy*x[k]-y[k])<=eps) f[i][j]|=1<<k-1;
}
}
for(i=0;i<(1<<n)-1;i++){
for(j=1;j<=n;j++) dp[i|f[g[i]][j]]=min(dp[i|f[g[i]][j]],dp[i]+1);
for(j=1;j<=n;j++) dp[i|(1<<j-1)]=min(dp[i|(1<<j-1)],dp[i]+1);
}
printf("%d\n",dp[(1<<n)-1]);
}
}