1. 程式人生 > >noip 2016(憤怒的小鳥)(狀壓dp)

noip 2016(憤怒的小鳥)(狀壓dp)

狀壓dp,每位代表一隻豬,1為豬打到了,0為豬還沒打到

兩個豬的座標確定一條拋弧線(即為打出鳥的軌跡)

g[i][j]表示由i豬和j豬確定的拋弧線,能打到的豬。(是一個二進位制的狀態)

預處理:把能被i豬和j豬確定的這條拋弧線能打到的豬都用狀壓的1來表示,存到g[i][j]裡。

dp[s]表是當狀態為s時所需要的最少拋弧線 (最少小鳥);

當發射軌跡為i豬和j豬確定的拋弧線的小鳥時          dp[s|g[i][j]]>dp[s]+1, dp[s|g[i][j]]=dp[s]+1;

求需要最少的拋物線數量(最少小鳥),把dp賦初值為很大的數

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int g[20][20],dp[1<<20],n,m;
double x23[20],y23[20];
void yuchuli(int zhu1,int zhu2,double x1f,double x1,double y1,double x2f,double x2,double y2)
{
	double x=x1f/x1*x2;
	double xx=x1/x1*x2;
	double y=y1/x1*x2;
	double a=(y-y2)/(x-x2f);
	x=x1f/x1f*x2f;
	xx=x1/x1f*x2f;
	y=y1/x1f*x2f;
	double b=(y-y2)/(xx-x2);
	if(a>=0) return ;//a>=0沒有任何豬能打掉,不合法
	for(int i=1;i<=n;i++)
	if(abs(a*x23[i]*x23[i]+b*x23[i]-y23[i])<=1e-6)//abs是必要的負數可以負很多
	g[zhu1][zhu2]|=(1<<(i-1)) ;
}
int main()
{
	//freopen("angrybirds.in","r",stdin);
//	freopen("angrybirds.out","w",stdout);
	int t;
	cin>>t;
	for(int z=1;z<=t;z++)
	{
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++)
		scanf("%lf%lf",&x23[i],&y23[i]);
     memset(dp,127,sizeof(dp));
     memset(g,0,sizeof(g));
     for(int i=1;i<=n;i++)
	 for(int j=1;j<=n;j++)
	{
	if(i!=j)yuchuli(i,j,x23[i]*x23[i],x23[i],y23[i],x23[j]*x23[j],x23[j],y23[j]);	//拋物線,兩個點確定一條拋物線
	}
     for(int i=1;i<=n;i++)
       g[i][i]=(1<<(i-1));
      dp[0]=0;
      for(int s=0;s<=(1<<n)-1;s++)
     for(int i=1;i<=n;i++)
      for(int j=1;j<=n;j++)
	 if(dp[s|g[i][j]]>1+dp[s])
       dp[s|g[i][j]]=1+dp[s];
     printf("%d\n",dp[(1<<n)-1]);
   	} 
	return 0;    
}