/*
思路:
首先这是一个重复覆盖问题
我们把每一个点都看成一个二进制位
这一类状态压缩重要的就是预处理
本题的预处理:
path数组:path[i][j]是表示那个过i,j小猪的抛物线,值是一个 state 表示这个抛物线可以打掉那些小猪
这里涉及到抛物线,首先是解出来a,b
在之后是依次判断哪个小猪可以被打掉,抛物线要满足开口向下,并且斜率不能是无穷
再之后是常规又不一样的dp
s[state]是打掉这个状态的所有抛物线组合方式
首先这里的f[state]是表示要打掉这个状态的这些小猪所需要的最小次数
借助搜索的思想
依次遍历每一个状态,遍历这个状态中所有没有被打掉的小猪,并选择一条抛物线打掉这个小猪
那么更新后的状态就是 newstate,这时也是f[newstate]可能发生改变的时刻,要取一个min
到时候试一下搜索技术
目标状态是要所有点被覆盖,那么对于任意一个点来说都会被某条抛物线覆盖
枚举点t的时候,就会枚举所有能更新出点t的抛物线,以此来找到最终状态所在的集合
这样保证了所有抛物线都会被枚举到,那具体枚举哪一个点就不是那么重要
*/
#include<bits/stdc++.h>
using namespace std;
const int N=20,M=1<<N;
const double eps=1e-8;
#define x first
#define y second
int f[M],path[N][N];
typedef pair<double, double> PDD;
PDD q[N];
bool cmp(double a,double b)
{
if(fabs(a-b)<eps) return true;
else return false;
}
int main()
{
int t;
cin>>t;
while(t--){
memset(path,0,sizeof path);
int n,m;
cin>>n>>m;
for(int i=0;i<n;++i) cin>>q[i].x>>q[i].y;
for(int i=0;i<n;++i){//读入了所有的小猪了hhh我们要开始搞出他们的抛物线
path[i][i] = 1<<i;
double x1=q[i].x, y1=q[i].y;
for(int j=0;j<n;++j )
{
double x2=q[j].x,y2=q[j].y;
if(cmp(x1,x2))continue;//不能组成抛物线的情况
double a=(y1 / x1 - y2 / x2) / (x1 - x2);
double b= y1/x1 - a*x1;
if(cmp(a,0.0)) continue;
if(a>0.0) continue;
int state=0;
for(int k=0 ;k<n;++k)
{
double x3=q[k].x,y3=q[k].y;
if(cmp(a*x3*x3+b*x3,y3)) state += 1<<k;
}
path[i][j] = state;
}
}
//下面遍历所有的状态,看一下这个状态里面一个没有被打掉的小猪枚举所有能打掉这个小猪的抛物线,干掉他并且更新f
memset(f,0x3f,sizeof f);//因为要求最小值
f[0] = 0;
for(int i=0;i +1 < 1<<n;++i)
{
int x=0;
for(int j=0;j< n; ++j){
if(i >> j & 1)continue;
else{
x=j;
break;
}
}
//x号小猪在这个状态里面没有被打掉
for(int k=0;k<n;++k)
{
int state = path[x][k];
f[i | state]=min(f[i|state],f[i]+1);
}
}
cout<<f[(1 <<n)-1]<<endl;
}
}