题意:原题在这
有一架弹弓位于 (0,0)(0,0) 处,每次 Kiana
可以用它向第一象限发射一只红色的小鸟,小鸟们的飞行轨迹均为形如 y=ax^2+bxy=ax2+bx 的曲线,其中 a,ba,b 是Kiana
指定的参数,且必须满足 a < 0a<0,a,ba,b 都是实数。
当小鸟落回地面(即 xx 轴)时,它就会瞬间消失。
在游戏的某个关卡里,平面的第一象限中有 nn 只绿色的小猪,其中第 ii 只小猪所在的坐标为 \left(x_i,y_i \right)(xi,yi)。
如果某只小鸟的飞行轨迹经过了 \left( x_i, y_i \right)(xi,yi),那么第 ii 只小猪就会被消灭掉,同时小鸟将会沿着原先的轨迹继续飞行;
如果一只小鸟的飞行轨迹没有经过 \left( x_i, y_i \right)(xi,yi),那么这只小鸟飞行的全过程就不会对第 ii 只小猪产生任何影响。
例如,若两只小猪分别位于 (1,3)(1,3) 和 (3,3)(3,3),Kiana
可以选择发射一只飞行轨迹为 y=-x^2+4xy=−x2+4x 的小鸟,这样两只小猪就会被这只小鸟一起消灭。
而这个游戏的目的,就是通过发射小鸟消灭所有的小猪。
这款神奇游戏的每个关卡对 Kiana
来说都很难,所以Kiana
还输入了一些神秘的指令,使得自己能更轻松地完成这个游戏。这些指令将在【输入格式】中详述。
假设这款游戏一共有 TT 个关卡,现在 Kiana
想知道,对于每一个关卡,至少需要发射多少只小鸟才能消灭所有的小猪。由于她不会算,所以希望由你告诉她。
思路:
1. 先枚举每一对瓷猪解出抛物线参数A,B,再在精度范围内更新shoot状态
2. shoot[20][20]表示经过i,j的抛物线能达到的瓷猪数量(状态)
3. 对于一条抛物线上的多个瓷猪:dp[i|shoot[j][k]]=min(dp[i|shoot[j][k]],dp[i]+1);
对于某些单独的瓷猪:dp[i|(1<<(j-1))]=min(dp[i|(1<<(j-1))],dp[i]+1);
代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<cmath> #define inf 999999999 using namespace std; const double eps=1e-7;//精度范围内的浮点数比较 int t,n,m; double A,B;//抛物线参数 int shoot[20][20];//表示经过i,j的抛物线能达到的瓷猪数量(状态) double x[20],y[20]; int dp[1<<20]; int main() { scanf("%d",&t); for (int f=1;f<=t;f++) { scanf("%d%d",&n,&m); memset(shoot,0,sizeof(shoot)); for(int i=0;i<(1<<20);i++) dp[i]=inf; dp[0]=0; for(int i=1;i<=n;i++) { scanf("%lf%lf",&x[i],&y[i]); } for (int i=1;i<n;i++) for (int j=i+1;j<=n;j++) { A=(y[i]*x[j]-y[j]*x[i])/(x[i]*x[i]*x[j]-x[j]*x[j]*x[i]); if (A>=0) continue; B=(y[i]-A*x[i]*x[i])/x[i]; for (int k=1;k<=n;k++) { if(abs(A*x[k]*x[k]+B*x[k]-y[k])<=eps) shoot[i][j]+=(1<<(k-1)); } } for(int i=0;i<(1<<n);i++) { if(dp[i]==inf) continue; for (int j=1;j<n;j++) for (int k=j+1;k<=n;k++) { dp[i|shoot[j][k]]=min(dp[i|shoot[j][k]],dp[i]+1); } for (int 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]); } }
题解里面也有一种方法很好理解但是写挂了,贴上思路及告瓷代码:
1.定义dp[i][j]表示开了i炮现在的情况是j(状态),的第k位为1表示这头猪已经被干掉。
dp[i][j]是一个bool,true表示i和j的情况存在。
那么我们每次转移是枚举每个可能的炮弹,还要特判一下只打一头猪的情况。
2.预处理
枚举任意两头猪,然后待定系数法解出a,b
之后枚举每一个点,判定是否可以被打到
最后输出的数是一个二进制数,表示这一炮可以打到
void formula(int t1,int t2) { double x1=pos[t1].x;double x2=pos[t2].x; double y1=pos[t1].y;double y2=pos[t2].y; pxa=(x2*y1-x1*y2)/(x1*x2*(x1-x2)); if(a>-eps) return; shoot[++cnt]=digit-1; pxb=(y1/x1)-a*x1; for(int i=1;i<=n;i++) { double xx=pos[i].x;double yy=pos[i].y; double temp=a*xx*xx+b*xx; if(temp-eps<yy&&yy<temp+eps) { shoot[cnt]|=(1<<i-1);//这头小猪凉凉 } if(vis[shoot[cnt]]==1) cnt--; else vis[shoot[cnt]]=1; return; } } void fuck() { for(int i=1;i<=t;i++) { scanf("%d%d",&n,&m); digit=1<<n; for(int j=1;j<=n;j++) { scanf("%lf%lf",&pos[j].x,&pos[j].y); } } cnt=0; memset(vis,0,sizeof(vis)); for(int t1=1;t1<=n;t1++) for(int t2=1;t2<t1;t2++) { formula(t1,t2); } memset(dp,0,sizeof(dp)); dp[0][digit-1]=1; for(int i=1;i<=n;i++) for(int j=0;j<digit;j++) { if(dp[i][j]==1)//如果存在 { for(int t1=1;t1<=n;t1++) { int q=(j|(1<<t1));//特判只打一头的情况 if(q==0)//第一个合法一定是最小 { printf("%d\n",i+1); return; } dp[i+1][q]=1; } for(int k=1;k<=cnt;k++)//枚举抛物线 { int p=shoot[k]&j; if(p==0) { printf("%d\n",i+1); return; } if(p!=j) { dp[i+1][p]=1; } } } } return; }