[NOIP2016 Day2 T3]愤怒的小鸟 Solution

题意:二维平面上有 n n n个点,现在需要用最少的形如 a x 2 + b x ax^2+bx ax2+bx抛物线去完全覆盖这些点。
首先预处理出来如果攻击这两个点能消灭的所有点的集合。表示为 k i l l [ i ] [ j ] kill[i][j] kill[i][j]
然后考虑 d p dp dp
d p [ i ] dp[i] dp[i]表示消灭 i i i这个集合的点需要的最少抛物线数。
转移:

  • d p [ i ∣ k i l l [ x ] [ y ] ] = min ⁡ ( d p [ i ∣ k i l l [ x ] [ y ] ] , d p [ i ] + 1 ) dp[i|kill[x][y]]=\min(dp[i|kill[x][y]],dp[i]+1) dp[ikill[x][y]]=min(dp[ikill[x][y]],dp[i]+1)
  • d p [ i ∣ ( 1 < < ( j − 1 ) ] = min ⁡ ( d p [ i ∣ ( 1 < < ( j − 1 ) ) ] , d p [ i ] + 1 ) dp[i|(1<<(j-1)]=\min(dp[i|(1<<(j-1))],dp[i]+1) dp[i(1<<(j1)]=min(dp[i(1<<(j1))],dp[i]+1)

考虑如何优化?
考虑无论怎么打,都需要打完所有的点,所以每次应当选择那个符合条件的最小的没打过的点,然后转移。
这部分也是可以预处理算出来的。
c o d e : code: code:

#include <bits/stdc++.h>
#define regi register int
int T;
int n;
int trans[2000000];
int Kill[20][20];
int f[2000000];
struct Pig{
	double x;
	double y;
}a[20];
main(){
	scanf("%d",&T);
	for(regi state=0,maxstate=1<<18;state<=maxstate;++state)
	  for(regi i=1;i<=18;++i)
	    if((state|(1<<i-1))>state){
	      trans[state]=i;
	      break;
	    }
	while(T--){
		scanf("%d%*d",&n);
		for(regi i=1;i<=n;++i)
			scanf("%lf%lf",&a[i].x,&a[i].y);
		memset(Kill,0,sizeof Kill);
		for(regi i=1;i<=n;++i)
		  for(regi j=1;j<=n;++j){
		  	if(fabs(a[j].x-a[i].x)<1e-8)
		  	  continue;
		  	double A=(a[j].y/a[j].x-a[i].y/a[i].x)/(a[j].x-a[i].x);
		  	double B=(a[j].y-A*a[j].x*a[j].x)/a[j].x;
		    if(A>-1e-8)
		      continue;
		  	Kill[i][j]=(1<<i-1)|(1<<j-1);
		  	for(regi k=1;k<=n;++k){
		  		if(fabs(a[k].y-A*a[k].x*a[k].x-B*a[k].x)<=1e-8)
		  		  Kill[i][j]|=(1<<k-1);
		  	}
		  }
		memset(f,0x3f,sizeof f);
		f[0]=0;
		for(regi state=0,maxstate=1<<n;state<maxstate;++state){
			for(regi i=1;i<=n;++i)
					f[state|(1<<i-1)]=std::min(f[state|(1<<i-1)],f[state]+1);
			for(regi i=1;i<=n;++i)
			    f[state|Kill[trans[state]][i]]=std::min(f[state|Kill[trans[state]][i]],f[state]+1);
		}
		printf("%d\n",f[(1<<n)-1]);
	}
	return 0;
}
/*
预处理每个状态最小转移点,保证时间复杂度
然后预处理出打这两个点能打到的其它点
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值