1. 程式人生 > 實用技巧 >洛谷 P2831 憤怒的小鳥 - 狀壓DP

洛谷 P2831 憤怒的小鳥 - 狀壓DP

洛谷:P2831 憤怒的小鳥

題目傳送門

本題是一道很明顯的狀壓DP,麻煩的是浮點數的處理和拋物線的計算,這裡講一些注意事項:

一.浮點數判相等:

二.拋物線的判斷:

題目中的拋物線有兩個未知數,在正常情況下,每兩點便可確定一條拋物線,但以下情況除外:

1.兩點所在的直線經過原點

2.兩點橫座標相等

3.a < 0

做法:

1、N3預處理出所有拋物線,及其能經過的點。

2、轉移。

其中還有許多優化,這裡我用的是最暴力的方法。

注:本題可用隨機化。

#include<cstdio>
#include<algorithm>
#include<cstring>
int para[650];
int tot;
#define inf 1000001
int min(int x,int y)
{
return x<y?x:y;
}
double Mx(double x,double y)
{
return x>y?x:y;
}
double abs(double x)
{
return x>0?x:-x;
}
int dp[1<<19];
double X[21],Y[21];
#define eps 1e-9
bool Be(double x,double y)
{
if(abs(x-y)<=eps*Mx(abs(x),abs(y)))
return true;
else
return false;

}int wuxian[600],jzw=0;
void init()
{
for(int i=0;i<=18;i++) X[i]=Y[i]=0;
for(int i=0;i<=(1<<19)-1;i++)
dp[i]=inf;
dp[0]=0;
jzw=tot=0;
for(int i=1;i<=400;i++)para[i] = 0;
}
double geta(double x1,double y1,double x2,double y2)
{
return (y1*x2-y2*x1)/(x1*x1*x2-x2*x2*x1);
}
double getb(double x1,double y1,double x2,double y2)
{
return (y1*x2*x2-y2*x1*x1)/(x1*x2*x2-x2*x1*x1);
}
double getk(double x1,double y1,double x2,double y2)
{
if(Be(x1,x2)) return inf;
return (y1-y2)/(x1-x2);
}
bool delta(double x1,double y1,double x2,double y2)
{
if(x1==x2) return 0 ;
double k=getk(x1,y1,x2,y2);
double b=y1-k*x1;
if(Be(b,0))
return 1;
if(Be(b,eps))
return 1;
return 0;
}
void build_dp(int n)
{
for(int i=0;i<n;i++)
{
double x1 = X[i+1];
double y1 = Y[i+1];
dp[1<<i] = 1;
// printf("%f %f",x1,y1);
for(int j=0;j<n;j++)
{
double x2 = X[j+1];
double y2 = Y[j+1];
if(x1==x2) continue;

double a = geta(x1,y1,x2,y2);
double b = getb(x1,y1,x2,y2);
// printf("%f ",a);
if(a>0 || delta(x1,y1,x2,y2)) continue ;
dp[(1<<j)|(1<<i)] = 1;
int sta=(1<<j)|(1<<i);
for(int k=0;k<n;k++)
{
double x3 = X[k+1];
double y3 = Y[k+1];
dp[1<<k] = 1;
double yy = a*x3*x3+b*x3;
if(Be(yy,y3))
{
sta|=1<<k;
dp[sta]=1;
}
}
}
}
}

void build_para(int n)
{
for(int i=1;i<=(1<<n)-1;i++)
{
if(dp[i]==1) para[++tot] = i;
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,m;
scanf("%d%d",&n,&m);
init();
for(int i=1;i<=n;i++)
scanf("%lf%lf",&X[i],&Y[i]);
build_dp(n);
build_para(n);
// for(int i=1;i<=tot;i++) printf("%d ",para[i]);
// for(int i=1;i<=tot;i++) printf("%d ",para[i]);
for(int i=1;i<=tot;i++)
{
int hit = para[i];
for(int j=0;j<=(1<<n)-1;j++)
dp[j|hit] = min(dp[j|hit],dp[j]+1);
}
printf("%d\n",dp[(1<<(n))-1]);
}
}