题目戳这里:https://www.luogu.org/problemnew/show/P2831#sub
看到数据很小。。。18。。。
状压呗~
把所有抛物线预处理一遍
0~n位状态每一位:0不打1打
两层循环两个点i,j
可以形成抛物线就搜一遍所有点看是否在抛物线上(n<=18就是任性~)
void cal(int i,int j)
{
double a,b,ans1;
int i1;
if (abs(x[i]-x[j])<eps || abs(x[i]*y[j]-x[j]*y[i])<eps)
return ;
a=(x[i]*y[j]-x[j]*y[i])/(x[i]*x[j]*(x[j]-x[i]));
if (a>0)
return ;
b=(x[j]*x[j]*y[i]-y[j]*x[i]*x[i])/(x[j]*x[i]*(x[j]-x[i]));
num++;
s[i][j]=(1<<i)+(1<<j);
f[s[i][j]]=1;
for (i1=0;i1<n;i1++)
{
if (i1==i || i1==j)
continue;
ans1=a*x[i1]*x[i1]+b*x[i1]-y[i1];
if (abs(ans1)<eps)
{
s[i][j]+=(1<<i1);
f[s[i][j]]=1;
}
}
}
然后就是状压基本步骤:
循环状态s
循环点i,j
公式:f[s1 | s[i][j]]=min(f[s1 | s[ i ][ j ] ] , f[s1] + 1);
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
struct node
{
double a,b;
int x,y;
};
int n,m;
double x[18+5],y[18+5];
int s[18+5][18+5];//预处理所有状态
int f[1<<18];//dp
double eps=1e-8;//误差许可
int num;
int fa[18+5];
node n1[1000+5];
void cal(int i,int j)
{
double a,b,ans1;
int i1;
if (abs(x[i]-x[j])<eps || abs(x[i]*y[j]-x[j]*y[i])<eps)
return ;
a=(x[i]*y[j]-x[j]*y[i])/(x[i]*x[j]*(x[j]-x[i]));
if (a>0)
return ;
b=(x[j]*x[j]*y[i]-y[j]*x[i]*x[i])/(x[j]*x[i]*(x[j]-x[i]));
num++;
s[i][j]=(1<<i)+(1<<j);
f[s[i][j]]=1;
for (i1=0;i1<n;i1++)
{
if (i1==i || i1==j)
continue;
ans1=a*x[i1]*x[i1]+b*x[i1]-y[i1];
if (abs(ans1)<eps)
{
s[i][j]+=(1<<i1);
f[s[i][j]]=1;
}
}
}
int main()
{
int i,t,j,s1;
freopen("a.txt","r",stdin);
scanf("%d",&t);
while (t!=0)
{
memset(s,0,sizeof(s));
memset(f,0x3f,sizeof(f));
t--; num=0;
scanf("%d%d",&n,&m);
for (i=0;i<n;i++)
{
scanf("%lf%lf",&x[i],&y[i]);//lf double类型
}
for (i=0;i<n;i++)
{
for (j=0;j<n;j++)
{
if (i==j)
{
s[i][i]=(1<<i);//只选自己
continue;
}
cal(i,j);
}
}
f[0]=0;
for (s1=0;s1<(1<<n);s1++)
{
for (i=0;i<n;i++)
{
if ((s1&(1<<i))>0)
continue;
for (j=0;j<n;j++)
{
f[s1|s[i][j]]=min(f[s1|s[i][j]],f[s1]+1);
}
}
}
printf("%d\n",f[s1-1]);
}
return 0;
}