[NOIp2016提高组]愤怒的小鸟
题目大意:
平面直角坐标系的第一象限有n(n<=18)个点,你可以每次给出一个形如y=ax^2+bx的函数把经过这条函数的点消掉,问消掉所有的点至少要多少函数?
思路:
枚举每两个点对,可以唯一确定一条函数,再枚举第三个点,判断一下是否会经过这条函数。
状态压缩一下记录每条函数能消掉那些点。
然后就是一个简单的状压DP。
一开始由于把fabs打成了abs,样例都过不去,调了半个下午。
洛谷上随便A,UOJ上被Extra数据hack掉了,把eps从1e-7改成1e-10就A了。
1 #include<cmath> 2 #include<cstdio> 3 #include<cctype> 4 #include<algorithm> 5 inline int getint() { 6 register char ch; 7 while(!isdigit(ch=getchar())); 8 register int x=ch^'0'; 9 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 10 return x; 11 } 12 const int N=18; 13 const double eps=1e-10; 14 struct Point { 15 double x,y; 16 }; 17 Point p[N]; 18 int sit[N*N],s; 19 int f[1<<N]; 20 inline double pow(const double &x) { 21 return x*x; 22 } 23 int main() { 24 for(register int T=getint();T;T--) { 25 const int n=getint();getint(); 26 for(register int i=0;i<n;i++) { 27 scanf("%lf%lf",&p[i].x,&p[i].y); 28 } 29 for(register int i=0;i<n;i++) { 30 sit[i]=1<<i; 31 } 32 s=n; 33 for(register int i=0;i<n;i++) { 34 for(register int j=i+1;j<n;j++) { 35 const double x1=p[i].x,x2=p[j].x,y1=p[i].y,y2=p[j].y; 36 if(pow(x1)*x2==pow(x2)*x1) continue; 37 const double a=(x2*y1/x1-y2)/(x1-x2)/x2,b=((pow(x2)*y1/pow(x1)-y2)/(pow(x2)/x1-x2)); 38 if(a>-eps) continue; 39 sit[s]=(1<<i)|(1<<j); 40 for(register int k=j+1;k<n;k++) { 41 if(fabs(a*pow(p[k].x)+b*p[k].x-p[k].y)<eps) sit[s]|=1<<k; 42 } 43 s++; 44 } 45 } 46 for(register int i=1;i<(1<<n);i++) { 47 f[i]=1e9; 48 } 49 for(register int i=0;i<(1<<n);i++) { 50 for(register int j=0;j<s;j++) { 51 f[i|sit[j]]=std::min(f[i|sit[j]],f[i]+1); 52 } 53 } 54 printf("%d\n",f[(1<<n)-1]); 55 } 56 return 0; 57 }