2018.11.02 洛谷P2831 愤怒的小鸟(状压dp)

183 篇文章 0 订阅
22 篇文章 0 订阅

传送门
状压一眼题。


直接 f [ i ] f[i] f[i]表示未选择状态为 i i i时的最小次数。
然后考虑现在怎么转移。
显然可以直接枚举消掉某一个点或者某两个点,复杂度 O ( n 2 2 n ) O(n^22^n) O(n22n)
由于这个集合里面的所有点最终都会被消掉,因此顺序并不重要。
于是可以强制这一步会消掉 l o w b i t ( i ) lowbit(i) lowbit(i)
那么当前有两个选择:

  1. 只消掉 l o w b i t ( i ) lowbit(i) lowbit(i)
  2. 还会消掉其它的。

第一种直接递归,第二种可以预处理数组 f [ i ] [ j ] f[i][j] f[i][j]表示如果构造一条过了 i , j i,j i,j的抛物线并消去上面的点,剩下的点的集合。
然后转移就很轻松了。
代码:

#include<bits/stdc++.h>
using namespace std;
const int N=20;
int T,TT,n,stat[N][N],f[1<<18],bit[19],mp[(1<<18)+1];
bool g[N][N];
struct Node{double x,y;}p[N];
inline bool check(int pos,double a,double b){return fabs(a*p[pos].x*p[pos].x+b*p[pos].x-p[pos].y)>1e-10;}
inline int lowbit(int x){return x&-x;}
inline int countbit(int x){int ret=0;while(x)x-=lowbit(x),++ret;return ret;}
inline int dfs(int sta){
	if(~f[sta])return f[sta];
	if(!sta)return f[sta]=0;
	if(sta==lowbit(sta))return f[sta]=1;
	f[sta]=countbit(sta);
	int i=lowbit(sta),staa=sta-lowbit(sta);
	f[sta]=min(f[sta],1+dfs(sta^i));
	while(staa){
		int j=lowbit(staa);
		if(!g[mp[i]+1][mp[j]+1])f[sta]=min(f[sta],1+dfs(sta&stat[mp[i]+1][mp[j]+1]));
		staa-=j;
	}
	return f[sta];
}
int main(){
	scanf("%d",&T),bit[0]=1,mp[1]=0;
	for(int i=1;i<=18;++i)bit[i]=bit[i-1]<<1,mp[bit[i]]=i;
	while(T--){
		memset(f,-1,sizeof(f)),memset(g,0,sizeof(g)),scanf("%d",&n),scanf("%d",&TT);
		for(int i=1;i<=n;++i)scanf("%lf%lf",&p[i].x,&p[i].y);
		for(int i=1;i<=n;++i)
			for(int j=1;j<=n;++j){
				if(i==j){g[i][j]=1;continue;}
				int sta=0;
				double a1=p[i].x*p[i].x,b1=p[i].x,c1=-p[i].y,a2=p[j].x*p[j].x,b2=p[j].x,c2=-p[j].y;
				double a,b;
				if(fabs(b1-b2*a1/a2)>1e-5)b=-(c1-c2*a1/a2)/(b1-b2*a1/a2),a=(-c1-b1*b)/a1;
				else{g[i][j]=1;continue;}
				if(a>-1e-5){g[i][j]=1;continue;}
				else for(int k=1;k<=n;++k)if(check(k,a,b))sta|=1<<(k-1);
				stat[i][j]=sta;
			}
		printf("%d\n",dfs((1<<n)-1));
	}
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值