憤怒的小鳥-狀壓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;
}

 

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