#include<cstdio>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<vector>
#include<string>
#include<queue>
#include<map>
#include<stack>
using namespace std;
int d[105][105]; //d[i][j]=x 第i个教室 第j个学生编号x
int cnt[105];
int mp[105][105];
int minn = 10000000;
//思路:遍历所有情况,加入这个学生,或不加入这个学生,中途剪枝
int n,m;
/*
void dfs(int no,int tot) //目前有tot个教室,考虑将编号no的学生编入教室
{
if(tot >= minn) return; //剪枝
if(no > n)
{
if(tot < minn) minn = tot;
return;
}
int f1 = 0;
for(int i=1;i<=tot;i++)
{
f1 = 0;
for(int j=1;j<=n;j++)
{
if(d[i][j]==1) //第i间教室有个编号为j的学生
{
if(mp[no][j] == 1) //j和no认识,no无法编入i教室
{
f1 = 1;
break;
}
}
}
if(f1 == 0) //第i间教室无人认识no
{
d[i][no]=1;
dfs(no+1,tot); //编入no
d[i][no]=0; //不编入no
}
}
//仍无法编入no
d[tot+1][no] = 1;
dfs(no+1,tot+1); //已经得到无法编入no的情况下的教室数目
d[tot+1][no] = 0; //错误点,没有重置
}
*/
//超时原因: d[i][j]=1 第i个教室 有编号为j的学生 ,这种存储结构导致每次都需要遍历n次
//改进方法: d[i][j]=x 第i个教室 第j个人的编号为x
void dfs(int no,int tot) //目前有tot个教室,考虑将编号no的学生编入教室
{
if(tot >= minn) return; //剪枝
if(no > n)
{
if(tot < minn) minn = tot;
return;
}
int ct = 0;
for(int i=1;i<=tot;i++)
{
ct = 0;
for(int j=1;j<=cnt[i];j++) //d[i][j]=x ,x为编号
{
if(mp[no][d[i][j]] == 1) //x和no认识,no无法编入i教室
break;
else
ct++;
}
if(ct == cnt[i]) //第i间教室无人认识no
{
cnt[i]++;
d[i][cnt[i]]=no;
dfs(no+1,tot); //编入no
cnt[i]--; //不编入no
}
}
//仍无法编入no
cnt[tot+1]++;
d[tot+1][cnt[tot+1]] = no;
dfs(no+1,tot+1); //已经得到无法编入no的情况下的教室数目
cnt[tot+1]--; //重置
}
int main()
{
scanf("%d%d",&n,&m);
memset(mp,0,sizeof(mp));
memset(cnt,0,sizeof(cnt));
for(int i=1;i<=n;i++)
{
cnt[i] = 0;
for(int j=1;j<=n;j++)
{
mp[i][j] = 0;
d[i][j] = 0;
}
}
for(int i=1;i<=m;i++)
{
int a,b;
scanf("%d %d",&a,&b);
mp[a][b] = 1;
mp[b][a] = 1;
}
dfs(1,1);
printf("%d",minn);
return 0;
}