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;
}
上一篇: GCD最大公约数
下一篇: 单例设计模式 - 懒汉式和饿汉式