[状压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);//全状态填满的鸟数 
	  }
	
}//多做不如精做 
posted @ 2022-02-17 21:01  liangchenming  阅读(50)  评论(1)    收藏  举报