/**
链接:http://poj.org/problem?id=3352
tarjan 边双联通分量求解;
题意:至少加几条边使原无向图边双联通。
双连通分量的求解:为缩点后图叶子节点个数(leaf) (leaf+1)/2;
*/
#include<iostream>
#include<algorithm>
#include<vector>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=1e5+7;
int top,bcnt,dcnt;
int sta[maxn],dfn[maxn],low[maxn],belong[maxn];
bool ins[maxn];
int out[maxn];
int u,v,n,m;
std::vector<int> G[maxn];
std::vector<int> GG[maxn];
int parent[maxn];//节点关系;
int iscut[maxn];//当前节点是否为割点;
int cut_num;//割点数量;
void dfs(int u){
dfn[u]=low[u]=++dcnt;
ins[u]=true;
sta[top++]=u;
int child=0;
int SZ=G[u].size();
//for(int v:G[u]){
for(int i=0;i<SZ;i++){
int v=G[u][i];
if(v==parent[u]) continue;//对于无向图而言的自环;
if(dfn[v]==0) {
child++;
parent[v]=u;
dfs(v);
low[u]=min(low[u],low[v]);
if((!iscut[u]&&parent[u]!=-1&&low[v]>=dfn[u])||(parent[u]==-1&&child>=2&&!iscut[u])) iscut[u]=1,cut_num++;//非根节点low[v]>=dfn[u],当前节点为根节点 且出度为2
}
else if(ins[v]) low[u]=min(low[u],dfn[v]);
}
/*******************************缩点操作部分 栈记录可能构成强联通分量的点**************/
if(dfn[u]==low[u]) {
++bcnt;
int pos;
do{
pos=sta[--top];
ins[pos]=false;
belong[pos]=bcnt;
}while(pos!=u);
}
return ;
}
void tarjan(){
bcnt=top=dcnt=cut_num=0;
memset(dfn,0,sizeof(dfn));
memset(ins,0,sizeof(ins));
memset(out,0,sizeof(out));
memset(iscut,0,sizeof(iscut));
memset(parent,-1,sizeof(parent));
for(int i=1;i<=n;i++) if(dfn[i]==0) dfs(i);
/***缩点后图变为GG操作*********************************/
for(int i=1;i<=n;i++) GG[i].clear();
for(int i=1;i<=n;i++){
int SZ=G[i].size();
//for(int pos:G[i]){
for(int j=0;j<SZ;j++){
int pos=G[i][j];
int u=belong[i];
int v=belong[pos];
if(u!=v) GG[u].push_back(v),++out[u];
}
}
}
int vis[1010][1010];
int main () {
scanf("%d %d",&n,&m);
memset(vis,0,sizeof(vis));
for(int i=1;i<=m;i++){
scanf("%d %d",&u,&v);
if(vis[u][v]) continue;vis[u][v]=vis[v][u]=1; //对于存在重复的边而言;
G[u].push_back(v),G[v].push_back(u);
}
tarjan();
int ret=0;
for(int i=1;i<=bcnt;i++) if(out[i]==1) ret++;
printf("%d\n",(ret+1)/2);
return 0;
}