NOIP2016愤怒的小鸟(状压Dp)

题目链接:题目链接

题目分析:

  我们发现n的范围很小,可以考虑记忆化搜索或者状态压缩,我们设f[i]表示打掉i状态里所有为1的位所表示的小猪得最小花费;

f[i]=min(f[i],f[i^(i&(G[j][k]))+1];

我们枚举状态i和小猪j,k其中G[][]表示i,j所形成的抛物线所能打掉的小猪的集合,^表示在状态i中除去一个状态也就是说i的状态是由i^(i&G[j][k])转移过来的,i&G[j][k]表示i的状态中被i,j所形成的抛物线所打掉的集合。

最后注意一下浮点情况,设一个EPS就好了;

G[][]需要预处理一下,公式加减消元;

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#define EPS 1e-8
int G[18][18],f[1<<20],T,n,m;
double x[18],y[18];
using namespace std;
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		memset(f,0,sizeof(f));
		memset(G,0,sizeof(G));
		memset(x,0,sizeof(x));
		memset(y,0,sizeof(y)); 
		scanf("%d%d",&n,&m);
		for(int i=0;i<n;i++) scanf("%lf%lf",&x[i],&y[i]);
		int sum=(1<<n)-1;
		for(int i=0;i<n;i++)//预处理每两个猪所形成的抛物线,并压缩其对其他猪的影响 
		{
			for(int j=0;j<n;j++)
			{
				if(i==j) continue ;
				double a=(x[j]*y[i]-x[i]*y[j])/(x[i]*x[j]*(x[i]-x[j]));
				double b=(x[j]*x[j]*y[i]-x[i]*x[i]*y[j])/(x[i]*x[j]*(x[j]-x[i]));
				if(a<=EPS)
				{
					for(int k=0;k<n;k++)
					{
						if(fabs(a*x[k]*x[k]+b*x[k]-y[k])<=EPS)
						{
							G[i][j]|=(1<<(k));
						}
					}
				}
			}
		}
		f[0]=0;
		for(int i=1;i<=sum;i++)//枚举每一种可能出现的状态 
		{
			int j,k;
			for( j=0;j<n;j++)
			{
				if(i&(1<<j)) break;//如果这只猪在当前状态里已经被消灭,break; 
			}
			f[i]=f[i^(1<<j)]+1;//更新状态; 
			for(k=0;k<n;k++)//更新这个猪和其他诸所组成的抛物线所可以达到的状态 
			{
				if(k!=j&&((1<<k)&i))//已经被消灭 
				f[i]=min(f[i],f[i^(i&G[j][k])]+1);//(i&G[i][j]表示i这个状态中可以被i,j所形成的抛物线打掉的猪),更新 
			}
		}
		printf("%d\n",f[sum]);//打掉所有的猪 
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值