【NOIP2016提高组Day 2】愤怒的小鸟

Description

【NOIP2016提高组Day 2】愤怒的小鸟_第1张图片

Input

【NOIP2016提高组Day 2】愤怒的小鸟_第2张图片

Output

【NOIP2016提高组Day 2】愤怒的小鸟_第3张图片

solution

这道题可以用状压DP来做,首先,我们先预处理出任意两个点所推出的抛物线,在找出这个抛物线所经过的点数,用一个二进制数来表示,例如经过i,j两点的抛物线经过了1,5,8点,就是g[i][j]=10001001。
然后再设f[i]为状态为i时所需要的最少抛物线的数量。此时可推出方程式f[i|g[j][l]]=min(f[i]+1,f[i|g[j][l]]),此时要注意,我们还要判断一下,可能有些点需要一个点用一条抛物线,所以在此从1枚举到n,就是f[i|(1<

code

#include
#define rg register int
#define con continue
using namespace std;
int T,n,m,g[20][20],f[1000005];
double x[20],y[20],a,b,t;
bool pd(double x,double y){
     
    return abs(x-y)<(1e-6); 
}
int main()
{
     
	scanf("%d",&T);
	while(T--)
	{
     
		scanf("%d%d",&n,&m);
		for(rg i=1;i<=n;i++) scanf("%lf%lf",&x[i],&y[i]);
		for(rg i=1;i<=n;i++) for(rg j=i+1;j<=n;j++) g[i][j]=0;
		for(rg i=1;i<=n;i++)
		{
     
			for(rg j=i+1;j<=n;j++)
			{
     
				t=a=b=0;
				if(x[i]==x[j]) con;
				a=(y[j]/x[j]-y[i]/x[i])*1.0/(x[j]-x[i])*1.0;
				if(a>=0) con;//a要是负数
				b=y[i]/x[i]*1.0-a*x[i]*1.0;
				//确定一条抛物线,y=axx+bx
				for(rg l=1;l<=n;l++)
					if(pd(y[l]/x[l],a*x[l]+b)) t+=(1<<(l-1));
				g[i][j]=t;
			}
		}
		//把所有可能的抛物线求出来
		for(rg i=1;i<=(1<<n);i++) f[i]=99999;
		f[0]=0;
		for(rg i=0;i<(1<<n);i++)
		{
     
			for(rg j=1;j<=n;j++)
			{
     
				if(!(i&(1<<(j-1))))
				{
     
					for(rg l=j+1;l<=n;l++)
					{
     
						f[i|g[j][l]]=min(f[i|g[j][l]],f[i]+1);
					}
					f[i|(1<<j-1)]=min(f[i|(1<<j-1)],f[i]+1);
				}
			}
		}
		//枚举所有抛物线的可能
		printf("%d\n",f[(1<<n)-1]);
	}
	return 0;
}

你可能感兴趣的:(题解)