NOIP2016 D2-T3 愤怒的小鸟
看了题解之后知道,是状压dp。
一、首先预处理一个$2^n$次方的fpow[]数组
fpow[0]=1; for(Rint i=1;i<=19;i++)fpow[i]=(fpow[i-1]<<1);
二、然后预处理一个sta[i][j],表示经过O,i,j三点的那条抛物线经过的所有的点的状态,处理时要注意:
1、抛物线的$a$值大于等于0的要剔除
2、横坐标相同的两点不可能在同一条抛物线上
3、注意精度
处理完后就可以状压dp了。
三、dp时首先把dp[]值赋为inf
方程:
chkmin(dp[i|sta[J][K]],dp[i]+1)
四、最后的优化:
1、就是我们没有必要枚举所有的$i$,其实不论如何,在$dp[i]$中第一个没有出现的猪,我们最后一定要打的,所以我们干脆就只枚举最后那个猪,这样可以快一点
2、找到第一只没打过的猪后,只需要枚举从它开始剩下的猪即可,不要从1开始:
for(Rint k=j+1;k<=n;k++){//即此循环只需从j+1开始,而不需从1枚举到n int J=min(j,k),K=max(j,k); chkmin(dp[i|sta[J][K]],dp[i]+1); }
五、Attention!(洛谷AC,UOJ WA)
六、最后上AC代码:
#include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<cmath> #define Rint register int #define mem(a,b) memset(a,(b),sizeof(a)) using namespace std; template<typename T> inline void read(T &x){ x=0;T w=1,ch=getchar(); while(!isdigit(ch)&&ch!='-')ch=getchar(); if(ch=='-')w=-1,ch=getchar(); while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar(); x=x*w; } template<typename T> inline T Min(T &x,T &y){return x<y?x:y;} template<typename T> inline void chkmin(T &x,T y){if(y<x)x=y;} inline double Abs(double x){return x<0?-x:x;} const int maxn=23,inf=0x3f3f3f3f; const double eps=1e-12,eeps=-1e-6; int fpow[21]; int n,m,sta[maxn][maxn],dp[1<<maxn]; double x[maxn],y[maxn]; inline void init(){ n=0; m=0; } inline void update_sta(int a,int b){ sta[a][b]=0; double x1=x[a],y1=y[a],x2=x[b],y2=y[b]; if(x1==x2)return; double A=(y1*x2-y2*x1)/(x1*x1*x2-x2*x2*x1); if(A>=eeps)return; double B=(y1-A*x1*x1)/x1; for(Rint i=1;i<=n;i++) if(Abs(A*x[i]*x[i]+B*x[i]-y[i])<eps) sta[a][b]|=fpow[i-1]; } int main(){ fpow[0]=1; for(Rint i=1;i<=19;i++)fpow[i]=(fpow[i-1]<<1); int TT; read(TT); while(TT--){ init(); read(n);read(m); for(Rint i=1;i<=n;i++)scanf("%lf%lf",&x[i],&y[i]); for(Rint i=1;i<=n;i++) for(Rint j=i+1;j<=n;j++) update_sta(i,j); for(Rint i=0;i<fpow[n];i++)dp[i]=inf; dp[0]=0; for(Rint i=0;i<fpow[n];i++){ if(dp[i]==inf)continue; for(Rint j=1;j<=n;j++){ if(!(i&fpow[j-1])){ for(Rint k=j+1;k<=n;k++){ int J=min(j,k),K=max(j,k); chkmin(dp[i|sta[J][K]],dp[i]+1); } chkmin(dp[i|fpow[j-1]],dp[i]+1); break; } } } printf("%d\n",dp[fpow[n]-1]); } return 0; }
假如得了97分的话,估计是精度问题
判断a的正负时要if(a<-1e-6)
判断是否是同一解要if(Abs(...)<=1e-12)
否则会炸extra test