P2831 愤怒的小鸟
看到数据范围就知道是搜索或状压DP
算了一波复杂度搜索好像过不了极限数据
搞状压
设 f [ i ] 表示所有猪的状态为 i (二进制下1表示死了,0表示没死)时需要的最少发射次数
设 p [ i ] [ j ] 存经过第 i 只猪和第 j 只猪的抛物线经过的猪的状态(可以$n^2$预处理出来,解方程都会吧..)
找到第一个没死的猪 i ,然后枚举所有其他没死的猪 j ,进行转移:
f [ i|p [ i ] [ j ] ] = min ( f [ i|p [ i ] [ j ] ],f [ i ] + 1 )
不用 n^2 枚举所有两只猪的 p [ i ] [ j ] ,因为第一头猪现在不死以后也要死,所以没有任何区别
当然还要考虑只经过一头猪的情况: f [ i|(1<<i-1) ] = min ( f[ i|(1<<i-1) ],f [ i ] +1 )
注意抛物线解析式中 a<0,记得判一下合法性
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; typedef long long ll; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const double eps=1e-8; const int N=300007; int n,m,T; struct data { double x,y; }d[27]; double a,b; inline void slove(double x1,double y1,double x2,double y2)//解方程 { double t=x2*x2/x1/x1; b=(y2-y1*t)/(x2-x1*t); a=(y1-x1*b)/x1/x1; } int p[27][27]; int f[N]; void pre() { memset(p,0,sizeof(p)); memset(f,0x3f,sizeof(f)); f[0]=0;//记得初始化 for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) { if(fabs(d[i].x-d[j].x)<eps) continue;//如果横坐标相同则无解 slove(d[i].x,d[i].y,d[j].x,d[j].y); if(a>=0) continue;//判断合法性 for(int k=1;k<=n;k++) if(fabs(a*d[k].x*d[k].x+b*d[k].x-d[k].y)<=eps)/*如果抛物线经过猪k就记录一下*/ p[i][j]|=(1<<k-1); } } int main() { T=read(); while(T--) { n=read(); m=read(); for(int i=1;i<=n;i++) scanf("%lf%lf",&d[i].x,&d[i].y); pre(); int mx=(1<<n)-1,pos; for(int i=0;i<=mx;i++) { for(int j=0;j<n;j++) if( !((i>>j)&1) ) { pos=j; break; }//找到第一只没死的猪 f[i|(1<<pos)]=min(f[i|(1<<pos)],f[i]+1);//单独考虑 for(int j=pos+1;j<n;j++)//与其他猪一起考虑 if( !((i>>j)&1) ) f[i|p[pos+1][j+1]]=min(f[i|p[pos+1][j+1]],f[i]+1);//注意p的下标 } printf("%d\n",f[mx]); } return 0; }