[状压dp]愤怒的小鸟
愤怒的小鸟
题面:
T关,每一关有n头猪和一个其实一点用没有的指令m(迷惑条件)
之后是每头猪的位置,从原点发射抛物线 y=ax2+bx,去打猪,求消灭所有猪需要的最小鸟数
注意位置都要用double存储
这道题可以说具有相当恶心的一点:调整精度,卡95分过不了,把精度给调成-6就行了,至于精度在哪调详见代码
思路
数据量很小,足见是状态压缩dp,两两点枚举,(因为这两个点连同原点可以知道二次函数表达式),之后依照你求出来的表达式来算哪些点会在这次被你击中。之后求操作维护一个can数组表示你所有可能添加的操作(你所有可能的发射后击中情况)。因为有的点可能姥姥不疼舅舅不爱,没有点和它凑对,那就把这点贡献的状态也加到can数组中,这就是预处理。之后用dp数组枚举的是消灭猪的状态(1为该猪已被消灭)所需要的最小小鸟数量,我们由现在的状态来维护未来,现在的状态或(注意是|这个或,不是亦或!)上我们的can的状态就是由当前状态的dp+1,简单吧?(简单你妹!我这一道题做了六个多小时……)
然后关于怎么推二次函数表达式,我们应该化成一元一次方程来推。之前老推错,这里推一下
\(a{x_1}^2+bx_1=y_1\)
\(a{x_2}^2+bx_2=y_2\)
算了,太不好写了,挖坑待更,反正这不是随便推的吗?
代码
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n,m;int T;
struct node{
double x;double y;
}d[500000];
bool flag[500][500];
bool pan[3000];
int cnt;
int dp[900000];//消灭该状态的肥猪最后需要的鸟数
const int wc=1e-6;
int can[800000];//有效的状态操作数
void DtoB(int d) {
if(d/2)
DtoB(d/2);
cout<<d%2;
}
void clean(int n)
{
for(int q=1;q<=n;q++)
{
d[q].x=d[q].y=0;
}
}
bool cw(double x,double y)
{
if(fabs(x-y)<=1e-6)//精度!
return true;
else
return false;
}
inline int getsum(int x)
{
int res=0;
while(x)
{
res+=(x&1);
x>>=1;//以1为卡口,注意等于号
}
return res;
}
int main()//枚举上一把的消灭量
{
scanf("%d",&T);double x,y;
while(T--)
{
scanf("%d%d",&n,&m);
clean(n);
for(int i=1;i<=n;i++)
{
scanf("%lf%lf",&x,&y);
d[i].x=x;
d[i].y=y;
}
int MAX=(1<<n)-1;//即包括这个玩意能把那些个猪杀死掉的东西
//某状态花费鸟儿的数量
memset(dp,0x3f3f3f,sizeof(dp));
memset(flag,0,sizeof(flag));
memset(pan,0,sizeof(pan));
memset(can,0,sizeof(can));
cnt=0;
int ans=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i==j||flag[i][j]==true)continue;//i,j者,不过是枚举两头猪构成的抛物线
flag[i][j]=flag[j][i]=true;
double x_1=d[i].x;double y_1=d[i].y;
double x_2=d[j].x;double y_2=d[j].y;
if(x_1==x_2&&y_1!=y_2)continue;
double a=(y_1*x_2-x_1*y_2)/(x_1*x_1*x_2-x_1*x_2*x_2);
double b=(y_1*x_2*x_2-x_1*x_1*y_2)/(x_1*x_2*x_2-x_1*x_1*x_2);
if(a>=0)continue;
pan[i]=true;pan[j]=true;//说明这两个点都被判断过了
int c=0;//暂求出一个状态,即两个鸟所直接连出的位置的猪会被消灭
for(int u=1;u<=n;u++)
{
double x=d[u].x;double y=d[u].y;
if(cw(a*x*x+b*x,y))//说明也在这条抛物线上
{
int dc=1<<(u-1);
c=c^dc;
}
}
//dp[c]=min(dp[c],1);//直接用一发就可以了
can[++cnt]=c;//新晋合法是这个!
}
}
for(int i=1;i<=n;i++)
if(!pan[i])
{
int d=1<<(i-1);
can[++cnt]=d;
//ad++;
}//如果某个点压根是姥姥不疼舅舅不爱的,把单个的点也看成可能被添加到区间里的
dp[0]=0;
for(int i=1;i<=cnt;i++)
{
for(int j=0;j<=MAX;j++)
{
int k=j|can[i];
dp[k]=min(dp[k],dp[j]+1);
}//从前往后找,更新未来
}
if(dp[MAX]<0x3f3f3f)
ans=dp[MAX];
printf("%d\n",ans);//全状态填满的鸟数
}
}//多做不如精做

浙公网安备 33010602011771号