P2831 [NOIP2016 提高组] 愤怒的小鸟(状态压缩dp)

*原题链接*

题意:多组测试数据,每组数据给定我们n个点,要求我们用最少的抛物线,覆盖给出的所有点。

首先所有抛物线都过原点,满足y=ax^2+bx,因此两个坐标点就可以唯一确定一条抛物线,我们可以枚举所有点对,计算出一条抛物线,若其合法(a< 0),则再计算出此条抛物线还能覆盖哪些点。所以我们设parabola_{i,j}表示ij号点确定出来的抛物线,所能覆盖到的点集,n很小,所以点集用二进制表示就行。

接下来进行转移,找到当前状态未覆盖到的一个点,枚举这个点所能确定的抛物线,递推或记忆化搜索均可,但我更喜欢写记忆化搜索,并且效率似乎更快。

时间复杂度O(T*n2^n),细节见代码:

#include<bits/stdc++.h>
using namespace std;
#define x first
#define y second
#define PDD pair<double,double>
const int N=19,M=(1<<N);
const double eps=1e-6;

int T,n,m,f[M],parabola[N][N];//f[state]表示从state状态到(1<<n)-1所需要的最少抛物线数量
PDD q[N];

int cmp(double x,double y){
	if(fabs(x-y)<eps) return 0;//x==y;
	if(x>y) return 1;
	return -1;
}

int dfs(int state){
	if(state==(1<<n)-1) return 0;
	if(f[state]) return f[state];//记搜
	int t,res=n*n;
	for(int i=0;i<n;i++){
		if(!((state>>i)&1)){t=i;break;}
	}
	for(int i=0;i<n;i++){
		if(!parabola[t][i]) continue;
		res=min(res,dfs(state|parabola[t][i])+1);
	}
	return f[state]=res;
}

int main(){
	
	ios::sync_with_stdio(false);
	cin>>T;
	while(T--){
		cin>>n>>m;
		memset(parabola,0,sizeof(parabola));
		for(int i=0;i<n;i++) cin>>q[i].x>>q[i].y;
		for(int i=0;i<n;i++){
			parabola[i][i]=(1<<i);
			for(int j=i;j<n;j++){
				double x1=q[i].x,y1=q[i].y,x2=q[j].x,y2=q[j].y;
				if(cmp(x1,x2)==0) continue;//若两点横坐标相等,肯定确定不了抛物线,continue避免RE
				double a=(y1/x1-y2/x2)/(x1-x2);
				double b=y1/x1-a*x1;
				if(cmp(a,0)>=0) continue;
				for(int k=0;k<n;k++){
					double x=q[k].x,y=q[k].y;
					if(cmp(a*x*x+b*x,y)==0) parabola[i][j]+=(1<<k);
				}
			}
		}
		memset(f,0,sizeof(f));
		cout<<dfs(0)<<endl;
	}
	
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值