愤怒的小鸟-状压dp

题目链接: https://www.luogu.org/problem/P2831

搜索待写

对于状压dp,f[s]表示只要一维表示杀死的猪猪的集合为s的情况下需要的最小代价。对于每一个猪猪,他所被杀死的那个抛物线即可以是只杀死了他自己(比如所有猪猪的横座标都相同那就只能都用了杀死一头猪的抛物线完成任务),也可以是同时杀死了2个猪猪,而在这同时可能还会杀死别的猪猪,所以我们用一个集合存一下杀死j,k猪猪的抛物线同时可以一网打尽其他那些猪猪,注意这个集合里也包含了j,k两头猪猪。

这样状压转移s的时候就枚举一个不在集合里的猪,s可以加上某个猪或者加带这头猪的一条线

再考虑优化:枚举不在集合里的猪那个步骤其实可以去掉。在这一状态下,可以只加入第一个还没有加入集合的猪猪(即那个lowbit数组干的事情),因为你在之后的转移中迟早也把他搞到集合里面,还不如现在就考虑这个猪猪。(就好像小时候学的揹包时候也是按顺序转移的没必要枚举顺序对吧)

#include <bits/stdc++.h>

using namespace std;
const double eps=1e-8;
int T,n,m;int f[1<<20],lowbit[1<<20],lines[20][20]; 
double x[20],y[20]; 
void equation(double &x,double &y,double a1,double b1,double c1,double a2,double b2,double c2)
{ //解方程
    y=(a1*c2-a2*c1)/(a1*b2-a2*b1);
    x=(c1-b1*y)/a1;
}
int main()
{
	 for(int i=0;i<(1<<18);i++)
	 { //预处理lowunbit
        int j=1;
        for(;j<=18 && i&(1<<(j-1));j++);
        lowbit[i]=j;
    }
	scanf("%d",&T);
	for(int ti=1;ti<=T;ti++)
	{
		memset(lines,0,sizeof(lines));
		memset(f,0x3f,sizeof(f));f[0]=0;
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++) scanf("%lf%lf",x+i,y+i);
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				if(fabs(x[i]-x[j])<eps) continue;
				double a,b;
				equation(a,b,x[i]*x[i],x[i],y[i],x[j]*x[j],x[j],y[j]);
				if(a>-eps) continue;//a必须<0 
				for(int k=1;k<=n;k++)
                    if(fabs(a*x[k]*x[k]+b*x[k]-y[k])<eps) lines[i][j]|=(1<<(k-1));
			}
		}
		for(int i=0;i<(1<<n);i++)
		{
			int j=lowbit[i];
			f[i|(1<<(j-1))]=min(f[i|(1<<(j-1))],f[i]+1);//第j个单独加入或者加带j的一条线 
			for(int k=1;k<=n;k++) f[i|lines[j][k]]=min(f[i|lines[j][k]],f[i]+1); 
		}
		printf("%d\n",f[(1<<n)-1]);
	}
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章