愤怒的小鸟

@

题意

愤怒的小鸟

题解

这道题目不是一道SB的状压吗。

我们设\(f[i]\),表示射击状态是\(i\),最少用了多少直线。

\(i\)的第\(j\)位为\(1\)表示射击这个位置。

然后对于每个直线跑背包。

时间复杂度:\(O(Tn^22^n)\)

但是其实有更快做法,限制\(i\)只能被包含最低位的\(1\)的直线所更新,那么就是\(n\)条直线,所以就是\(O(Tn2^n)\)

话说我为什么要打莫比乌斯反演

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#define  N  20
#define  NN  300000
using  namespace  std;
template  <class  T>
inline  T  mymax(T  x,T  y){return  x>y?x:y;}
template  <class  T>
inline  T  mymin(T  x,T  y){return  x<y?x:y;}
double  f[10][10];
bool  gsxy()
{
	//有两行相同的会被完全消掉 
	int  n=2;
	for(int  i=1;i<n;i++)
	{
		double  mmax=0;int  id=0;
		for(int  j=i;j<=n;j++)
		{
			if(fabs(f[j][i])>=mmax)mmax=fabs(f[j][i]),id=j;
		}
		if(mmax<=1e-6)return  0;
		if(id!=i)swap(f[id],f[i]);
		for(int  j=i+1;j<=n;j++)
		{
			double  ss=f[j][i]/f[i][i];
			for(int  k=n+1;k>=i;k--)f[j][k]-=ss*f[i][k];
		}
	} 
	for(int  i=n;i>=1;i--)
	{
		if(f[i][i]<=1e-6)return  0;
		f[0][i]=f[i][n+1]/f[i][i];
		for(int  j=i-1;j>=1;j--)f[j][n+1]-=f[j][i]*f[0][i];
	}
	return  1;
}//高斯消元
//那么解析式就是f[0][1]x^2+f[0][2]x+f[0][3]
struct  node
{
	double  x,y;
}a[N];
int  dp[NN],ff[NN];//dp[x]表示的是覆盖x的最少直线数目 
int  n,m;
int  main()
{
	int  fuck=(1<<18)-1;
	for(int  i=2;i<=fuck;i++)ff[i]=ff[i>>1]+1;
	int  T;scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&n,&m);
		for(int  i=1;i<=n;i++)scanf("%lf%lf",&a[i].x,&a[i].y);/*为了防止全部都在一条直线的情况*/
		int  limit=(1<<n)-1;/*修BUG*/
		for(int  i=1;i<=limit;i++)dp[i]=dp[i^(i&-i)]+1;
		for(int  i=1;i<=n;i++)//和(0,0) 
		{
			for(int  j=i+1;j<=n;j++)
			{
				if(dp[(1<<(i-1))+(1<<(j-1))]!=1)
				{
					f[1][1]=a[i].x*a[i].x;f[1][2]=a[i].x;f[1][3]=a[i].y;
					f[2][1]=a[j].x*a[j].x;f[2][2]=a[j].x;f[2][3]=a[j].y;
					if(!gsxy())continue;
					if(f[0][1]<=-1e-6)//因为必须a<0 
					{
						int  x=(1<<(i-1))+(1<<(j-1));
						for(int  o=j+1;o<=n;o++)
						{
							if(fabs(f[0][1]*a[o].x*a[o].x+f[0][2]*a[o].x-a[o].y)<=1e-6)x+=(1<<(o-1));//表示这条直线可以覆盖更多的人 
						}
						for(int  o=x;o;o=(o-1)&x)dp[o]=1;
						int  k=limit^x;
						for(int  o=k;o;o=(o-1)&k)
						{
							for(int  l=(x-1)&x;l;l=(l-1)&x)
							{
								int  p=o^l;
								dp[o^x]=mymin(dp[o^x],dp[p]+dp[x]);
							}
							dp[o^x]=mymin(dp[o^x],dp[o]+dp[x]);
						}
					}
				}
			}
		}
		printf("%d\n",dp[limit]);
	}
	return  0;
}
posted @ 2019-11-17 16:16  敌敌畏58  阅读(163)  评论(0编辑  收藏  举报