洛谷2831 愤怒的小鸟

题目戳这里:https://www.luogu.org/problemnew/show/P2831#sub

 

看到数据很小。。。18。。。

状压呗~

把所有抛物线预处理一遍

0~n位状态每一位:0不打1打

两层循环两个点i,j 

可以形成抛物线就搜一遍所有点看是否在抛物线上(n<=18就是任性~)

void cal(int i,int j)
{
	double a,b,ans1;
	int i1;
	
	if (abs(x[i]-x[j])<eps || abs(x[i]*y[j]-x[j]*y[i])<eps)
		return ;
	a=(x[i]*y[j]-x[j]*y[i])/(x[i]*x[j]*(x[j]-x[i]));
	if (a>0)
		return ;
	b=(x[j]*x[j]*y[i]-y[j]*x[i]*x[i])/(x[j]*x[i]*(x[j]-x[i]));
	num++;
	
	s[i][j]=(1<<i)+(1<<j);
	f[s[i][j]]=1;
	for (i1=0;i1<n;i1++)
	{
		if (i1==i || i1==j)
			continue;
		ans1=a*x[i1]*x[i1]+b*x[i1]-y[i1];
		if (abs(ans1)<eps)
		{
			s[i][j]+=(1<<i1);
			f[s[i][j]]=1;
		}
	}
}

然后就是状压基本步骤:

循环状态s

循环点i,j

公式:f[s1 | s[i][j]]=min(f[s1 | s[ i ][ j ] ] , f[s1] + 1);

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>

using namespace std;

struct node
{
	double a,b;
	int x,y;
};

int n,m;
double x[18+5],y[18+5];
int s[18+5][18+5];//预处理所有状态 
int f[1<<18];//dp
double eps=1e-8;//误差许可
int num; 
int fa[18+5];
node n1[1000+5];


void cal(int i,int j)
{
	double a,b,ans1;
	int i1;
	
	if (abs(x[i]-x[j])<eps || abs(x[i]*y[j]-x[j]*y[i])<eps)
		return ;
	a=(x[i]*y[j]-x[j]*y[i])/(x[i]*x[j]*(x[j]-x[i]));
	if (a>0)
		return ;
	b=(x[j]*x[j]*y[i]-y[j]*x[i]*x[i])/(x[j]*x[i]*(x[j]-x[i]));
	num++;
	
	s[i][j]=(1<<i)+(1<<j);
	f[s[i][j]]=1;
	for (i1=0;i1<n;i1++)
	{
		if (i1==i || i1==j)
			continue;
		ans1=a*x[i1]*x[i1]+b*x[i1]-y[i1];
		if (abs(ans1)<eps)
		{
			s[i][j]+=(1<<i1);
			f[s[i][j]]=1;
		}
	}
}

int main()
{
	int i,t,j,s1;
	
	freopen("a.txt","r",stdin);
	scanf("%d",&t);
	while (t!=0)
	{
		memset(s,0,sizeof(s));
		memset(f,0x3f,sizeof(f));
		t--;	num=0;
		scanf("%d%d",&n,&m);
		for (i=0;i<n;i++)
		{
			scanf("%lf%lf",&x[i],&y[i]);//lf double类型 
		}
		for (i=0;i<n;i++)
		{
			for (j=0;j<n;j++)
			{
				if (i==j)
				{
					s[i][i]=(1<<i);//只选自己 
					continue;
				}
				cal(i,j);
			}
		}
		f[0]=0;
		for (s1=0;s1<(1<<n);s1++)
		{
			for (i=0;i<n;i++)
			{
				if ((s1&(1<<i))>0)
					continue;
				for (j=0;j<n;j++)
				{
					f[s1|s[i][j]]=min(f[s1|s[i][j]],f[s1]+1);
				}
			}
		}
		printf("%d\n",f[s1-1]);
	}
	
	
	
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值