欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

NOIP2016愤怒的小鸟

程序员文章站 2024-03-20 12:03:34
...

设dp[S]表示已经打了的猪的的序号组成的集合,那么我们枚举一个i表示这一次要打个猪,然后再枚举一个j,表示这一次把i和j一起打掉,那么预处理一个bit数组使得bit[i][j]表示以i和j的坐标确定的抛物线可以打掉的所有的猪,那么就由dp[S]+1转移到了dp[S|bit[i][j]]了,最后的答案就是dp[2^n-1].

#include <bits/stdc++.h> 
#define N 20  
#define eps 1e-8 
#define INF 0x3f3f3f3f
using namespace std;
int T,n,m;   
int g[N][N],f[1<<20]; //g存选择猪i j f存状压结果  
double x[N],y[N],a,b; //前向星存猪 
bool same(double a,double b) { return fabs(a-b)<=eps; } //精度判重   
int mymin(int a,int b) { return a<b?a:b; }
void Clear() //初始化 
{
    for(int i=1;i<=n;i++) 
      for(int j=1;j<=n;j++) 
        g[i][j]=0;    
    int k=1<<n; 
    for(int i=1;i<k;i++) 
      f[i]=INF; 
}
int main()
{
  scanf("%d",&T); 
  while(T--) 
  {
    scanf("%d%d",&n,&m);  
      Clear();        
      for(int i=1;i<=n;i++)  scanf("%lf%lf",&x[i],&y[i]); 
      for(int i=1;i<n;i++)
      for(int j=i+1;j<=n;j++) 
      {
          if(same(x[i],x[j])) continue; //如果横坐标相同一定打不了   
          a=(y[i]/x[i]-y[j]/x[j])/(x[i]-x[j]); 
          if(a>=0) continue; 
          b=y[i]/x[i]-a*x[i]; 
          for(int k=1;k<=n;k++)  //所有符合的都加进去  
            if(same(a*x[k]+b,y[k]/x[k])) 
              g[i][j]|=(1<<(k-1)); //01000101 表示能打1 3 7 这三只猪   
      } 
    f[0]=0; //边界条件 一只都不打   
    int k=1<<n; 
    for(int i=0;i<k;i++)   //每一种状态  
      for(int j=1;j<=n;j++) // 每一只猪  
        if(!(i & (1<<(j-1))))   // 能增加打掉的猪 i的状态那个位置是0  
        {
            for(int k=j;k<=n;k++) //前面的都打过了 
            { 
              //特判   从直接打掉和加一条中选取最小值
              if(k==j)
              {
                  f[i|(1<<(j-1))]=mymin(f[i|(1<<(j-1))],f[i]+1); 
                  continue; 
               }  
              if(same(x[k],x[j])) continue; //横坐标相同直接过不存在 
              f[i|g[j][k]]=mymin(f[i|g[j][k]],f[i]+1);  //选取该条抛物线 或者加一条  
            } 
            break; 
        } 
    printf("%d\n",f[(1<<n)-1]);  
  } 
  return 0;
}