题意:多组测试数据,每组数据给定我们个点,要求我们用最少的抛物线,覆盖给出的所有点。
首先所有抛物线都过原点,满足,因此两个坐标点就可以唯一确定一条抛物线,我们可以枚举所有点对,计算出一条抛物线,若其合法(
),则再计算出此条抛物线还能覆盖哪些点。所以我们设
表示
和
号点确定出来的抛物线,所能覆盖到的点集,
很小,所以点集用二进制表示就行。
接下来进行转移,找到当前状态未覆盖到的一个点,枚举这个点所能确定的抛物线,递推或记忆化搜索均可,但我更喜欢写记忆化搜索,并且效率似乎更快。
时间复杂度,细节见代码:
#include<bits/stdc++.h>
using namespace std;
#define x first
#define y second
#define PDD pair<double,double>
const int N=19,M=(1<<N);
const double eps=1e-6;
int T,n,m,f[M],parabola[N][N];//f[state]表示从state状态到(1<<n)-1所需要的最少抛物线数量
PDD q[N];
int cmp(double x,double y){
if(fabs(x-y)<eps) return 0;//x==y;
if(x>y) return 1;
return -1;
}
int dfs(int state){
if(state==(1<<n)-1) return 0;
if(f[state]) return f[state];//记搜
int t,res=n*n;
for(int i=0;i<n;i++){
if(!((state>>i)&1)){t=i;break;}
}
for(int i=0;i<n;i++){
if(!parabola[t][i]) continue;
res=min(res,dfs(state|parabola[t][i])+1);
}
return f[state]=res;
}
int main(){
ios::sync_with_stdio(false);
cin>>T;
while(T--){
cin>>n>>m;
memset(parabola,0,sizeof(parabola));
for(int i=0;i<n;i++) cin>>q[i].x>>q[i].y;
for(int i=0;i<n;i++){
parabola[i][i]=(1<<i);
for(int j=i;j<n;j++){
double x1=q[i].x,y1=q[i].y,x2=q[j].x,y2=q[j].y;
if(cmp(x1,x2)==0) continue;//若两点横坐标相等,肯定确定不了抛物线,continue避免RE
double a=(y1/x1-y2/x2)/(x1-x2);
double b=y1/x1-a*x1;
if(cmp(a,0)>=0) continue;
for(int k=0;k<n;k++){
double x=q[k].x,y=q[k].y;
if(cmp(a*x*x+b*x,y)==0) parabola[i][j]+=(1<<k);
}
}
}
memset(f,0,sizeof(f));
cout<<dfs(0)<<endl;
}
return 0;
}