CodeEff/ECCO huggingface dataset:
# Original solution
N = int(eval(input()))
L = []

for i in range(N):
  l = list(map(int,input().split()))
  L.append(l)
  
dp = [[0,0,0] for i in range(N)]
dp[0] = [L[0][0],L[0][1],L[0][2]]

for i in range(1,N):
  for j in range(3):
    dp[i][j] = max(dp[i - 1][(j + 1) % 3],dp[i - 1][(j + 2) % 3]) + L[i][j]
    
print((max(dp[N - 1])))

# Optimized solution
N = int(eval(input()))
L = [list(map(int,input().split())) for i in range(N)]

dp = [[0,0,0] for i in range(N)]
dp[0] = L[0]

for i in range(1,N):
  for j in range(3):
    dp[i][j] = max(dp[i - 1][(j + 1) % 3],dp[i - 1][(j + 2) % 3]) + L[i][j]
    
print((max(dp[-1])))
# Original solution
N=int(eval(input()))
dp=[[0 for i in range(N+1)] for i in range(3)]

for j in range(N):
    abc=list(map(int,input().split()))
    for i in range(3):
        if j==0:
            dp[i][j]=abc[i]
        else:
            dp[i][j]=max([dp[k][j-1]+abc[i] for k in range(3) if k!=i])
print((max([dp[i][N-1] for i in range(3)])))

# Optimized solution
N = int(eval(input()))

dp = list(map(int, input().split()))
for i in range(1, N):
    a, b, c = list(map(int, input().split()))
    newDP = [0] * 3
    newDP[0] = max(dp[1], dp[2]) + a
    newDP[1] = max(dp[0], dp[2]) + b
    newDP[2] = max(dp[0], dp[1]) + c

    dp = newDP[::]

ans = max(dp)
print(ans)
# Original solution
N = int(eval(input()))
A, B, C = [], [], []
for n in range(N):
    a, b, c = list(map(int, input().split()))
    A.append(a)
    B.append(b)
    C.append(c)
a, b, c = 0, 0, 0
for n in range(N):
    a, b, c = A[n] + max(b, c), B[n] + max(c, a), C[n] + max(a, b)
print((max(a, b, c)))


# Optimized solution
N = int(eval(input()))
a, b, c = 0, 0, 0
for n in range(N):
    an, bn, cn = list(map(int, input().split()))
    a, b, c = an + max(b, c), bn + max(c, a), cn + max(a, b)
print((max(a, b, c)))
# Original solution
import math,sys,bisect,heapq
from collections import defaultdict,Counter,deque
from itertools import groupby,accumulate
#sys.setrecursionlimit(200000000)
input = iter(sys.stdin.buffer.read().decode().splitlines()).__next__
ilele = lambda: list(map(int,input().split()))
alele = lambda: list(map(int, input().split()))
def list2d(a, b, c): return [[c] * b for i in range(a)]
#def list3d(a, b, c, d): return [[[d] * c for j in range(b)] for i in range(a)]
MOD = 1000000000 + 7
def Y(c):  print((["NO","YES"][c]))
def y(c):  print((["no","yes"][c]))
def Yy(c):  print((["No","Yes"][c]))

x,y,z = 0,0,0
for _ in range(int(eval(input()))):
    a,b,c = ilele()
    if x == y == z == 0:
        x,y,z = a,b,c
    else:
        d = a + max(y,z)
        e = b + max(x,z)
        f = c + max(x,y)
        x,y,z = d,e,f
print((max(x,y,z)))
    

# Optimized solution
import math,sys,bisect,heapq
from collections import defaultdict,Counter,deque
from itertools import groupby,accumulate
#sys.setrecursionlimit(200000000)
input = iter(sys.stdin.buffer.read().decode().splitlines()).__next__
ilele = lambda: list(map(int,input().split()))
alele = lambda: list(map(int, input().split()))
def list2d(a, b, c): return [[c] * b for i in range(a)]
#def list3d(a, b, c, d): return [[[d] * c for j in range(b)] for i in range(a)]
MOD = 1000000000 + 7
def Y(c):  print((["NO","YES"][c]))
def y(c):  print((["no","yes"][c]))
def Yy(c):  print((["No","Yes"][c]))

x,y,z = 0,0,0
for _ in range(int(eval(input()))):
    a,b,c = ilele()
    d = a + max(y,z)
    e = b + max(x,z)
    f = c + max(x,y)
    x,y,z = d,e,f
print((max(x,y,z)))
# Original solution
"""
解説PDFを見ながら実装
https://img.atcoder.jp/abc075/editorial.pdf
"""

def dfs(v, graph, visited, n):
    # 出発点
    visited[v] = True
    for i in range(n):
        # 辺がない
        if graph[v][i] == False:
            continue
        # 訪問済み
        if visited[i] == True:
            continue
        # 辺があったらそこから訪問する
        dfs(i, graph, visited, n)


def main():
    N, M = list(map(int, input().split()))

    graph = []
    for _ in range(N):
        row = [False] * N
        graph.append(row)

    A = []
    B = []
    for _ in range(M):
        a, b = list(map(int, input().split()))
        a = a - 1
        b = b - 1
        A.append(a)
        B.append(b)
        graph[a][b] = True
        graph[b][a] = True
    # print(graph)

    ans = 0

    for i in range(M):
        # 辺を削除
        graph[A[i]][B[i]] = False
        graph[B[i]][A[i]] = False

        visited = [False] * N

        # 常に0番目の頂点から出発
        dfs(0, graph, visited, N)

        bridge = False
        for j in range(N):
            if visited[j] == False:
                bridge = True
        if bridge:
            ans += 1

        # 辺を戻す
        graph[A[i]][B[i]] = True
        graph[B[i]][A[i]] = True

    print(ans)

if __name__ == "__main__":
    main()

# Optimized solution

def main():
    N, M = list(map(int, input().split()))
    AB = []
    for _ in range(M):
        a, b = list(map(int, input().split()))
        AB.append([a-1, b-1])

    # グラフを表現する（辺のある・なし）
    graph = []
    for _ in range(N):
        graph.append([False] * N)

    for a, b in AB:
        graph[a][b] = True
        graph[b][a] = True
        
    visited = [False] * N

    def dfs(v):
        """v（頂点）からスタートする"""

        # 到達済みだったらreturn
        if visited[v]:
            return

        # 到達した印
        visited[v] = True

        # そのさきへ（vと繋がっている）
        edges = graph[v]
        for i, e in enumerate(edges):
            if e:
                dfs(i)

        return


    ans = 0
        
    # 辺を一つずつ消してみて、橋かどうかを調べる
    for a, b in AB:
        graph[a][b] = False
        graph[b][a] = False

        visited = [False] * N
        dfs(0)
        
        # 全てTrueというわけではない（Falseが一つでもある場合）
        if not all(visited):
            ans += 1

        graph[a][b] = True
        graph[b][a] = True

    print(ans)

if __name__ == "__main__":
    main()
# Original solution
#dpでできないかな？
import sys
input = sys.stdin.readline
sys.setrecursionlimit(10**7)
from collections import Counter, deque
from collections import defaultdict
from itertools import combinations, permutations, accumulate, groupby, product
from bisect import bisect_left,bisect_right
from heapq import heapify, heappop, heappush
from math import floor, ceil,pi
from operator import itemgetter
def I(): return int(eval(input()))
def MI(): return list(map(int, input().split()))
def LI(): return list(map(int, input().split()))
def LI2(): return [int(eval(input())) for i in range(n)]
def MXI(): return [[LI()]for i in range(n)]
def SI(): return input().rstrip()
def printns(x): print(('\n'.join(x)))
def printni(x): print(('\n'.join(list(map(str,x)))))
inf = 10**17
mod = 10**9 + 7

def dfs(x):
    for i in tree[x]:
        if al[i]<0:
            al[i]=0
            dfs(i)
    
        
    
n,m=MI()
lis=[LI() for i in range(m)]
#print(lis)
cou=0
for i in range(m):
    tree=[[] for i in range(n)]
    for j in range(m):
        if i!=j:
            tree[lis[j][0]-1].append(lis[j][1]-1)
            tree[lis[j][1]-1].append(lis[j][0]-1)
    count=0
    for k in range(n):
        al=[-1]*n
        al[k]=0
        dfs(k)
        #print(al)
        if sum(al)==0:
            count+=1
    if count!=n:
        cou+=1
print(cou)        

# Optimized solution
#dpでできないかな？
import sys
input = sys.stdin.readline
sys.setrecursionlimit(10**7)
from collections import Counter, deque
from collections import defaultdict
from itertools import combinations, permutations, accumulate, groupby, product
from bisect import bisect_left,bisect_right
from heapq import heapify, heappop, heappush
from math import floor, ceil,pi
from operator import itemgetter
def I(): return int(eval(input()))
def MI(): return list(map(int, input().split()))
def LI(): return list(map(int, input().split()))
def LI2(): return [int(eval(input())) for i in range(n)]
def MXI(): return [[LI()]for i in range(n)]
def SI(): return input().rstrip()
def printns(x): print(('\n'.join(x)))
def printni(x): print(('\n'.join(list(map(str,x)))))
inf = 10**17
mod = 10**9 + 7

def dfs(x):
    for i in tree[x]:
        if al[i]<0:
            al[i]=0
            dfs(i)
    
        
    
n,m=MI()
lis=[LI() for i in range(m)]
#print(lis)
cou=0
for i in range(m):
    tree=[[] for i in range(n)]
    for j in range(m):
        if i!=j:
            tree[lis[j][0]-1].append(lis[j][1]-1)
            tree[lis[j][1]-1].append(lis[j][0]-1)
    count=0
    for k in range(n):
        al=[-1]*n
        al[k]=0
        dfs(k)
        #print(al)
        if not -1 in al:
            count+=1
    if count!=n:
        cou+=1
print(cou)
# Original solution
#dpでできないかな？
import sys
input = sys.stdin.readline
sys.setrecursionlimit(10**7)
from collections import Counter, deque
from collections import defaultdict
from itertools import combinations, permutations, accumulate, groupby, product
from bisect import bisect_left,bisect_right
from heapq import heapify, heappop, heappush
from math import floor, ceil,pi
from operator import itemgetter
def I(): return int(eval(input()))
def MI(): return list(map(int, input().split()))
def LI(): return list(map(int, input().split()))
def LI2(): return [int(eval(input())) for i in range(n)]
def MXI(): return [[LI()]for i in range(n)]
def SI(): return input().rstrip()
def printns(x): print(('\n'.join(x)))
def printni(x): print(('\n'.join(list(map(str,x)))))
inf = 10**17
mod = 10**9 + 7

def dfs(x):
    for i in tree[x]:
        if al[i]<0:
            al[i]=0
            dfs(i)
    
        
    
n,m=MI()
lis=[LI() for i in range(m)]
#print(lis)
cou=0
for i in range(m):
    tree=[[] for i in range(n)]
    for j in range(m):
        if i!=j:
            tree[lis[j][0]-1].append(lis[j][1]-1)
            tree[lis[j][1]-1].append(lis[j][0]-1)
    count=0
    for k in range(n):
        al=[-1]*n
        al[k]=0
        dfs(k)
        #print(al)
        if not -1 in al:
            count+=1
    if count!=n:
        cou+=1
print(cou)    

# Optimized solution
#dpでできないかな？
import sys
input = sys.stdin.readline
sys.setrecursionlimit(10**7)
from collections import Counter, deque
from collections import defaultdict
from itertools import combinations, permutations, accumulate, groupby, product
from bisect import bisect_left,bisect_right
from heapq import heapify, heappop, heappush
from math import floor, ceil,pi
from operator import itemgetter
def I(): return int(eval(input()))
def MI(): return list(map(int, input().split()))
def LI(): return list(map(int, input().split()))
def LI2(): return [int(eval(input())) for i in range(n)]
def MXI(): return [[LI()]for i in range(n)]
def SI(): return input().rstrip()
def printns(x): print(('\n'.join(x)))
def printni(x): print(('\n'.join(list(map(str,x)))))
inf = 10**17
mod = 10**9 + 7

def dfs(x,tree,al):
    for i in tree[x]:
        if al[i]<0:
            al[i]=0
            dfs(i,tree,al)
    
        
def main():  
    n,m=MI()
    lis=[LI() for i in range(m)]
    #print(lis)
    cou=0
    for i in range(m):
        tree=[[] for i in range(n)]
        for j in range(m):
            if i!=j:
                tree[lis[j][0]-1].append(lis[j][1]-1)
                tree[lis[j][1]-1].append(lis[j][0]-1)
        count=0
        for k in range(n):
            al=[-1]*n
            al[k]=0
            dfs(k,tree,al)
            #print(al)
            if not -1 in al:
                count+=1
        if count!=n:
            cou+=1
    print(cou)    

if __name__ == '__main__':
    main()
# Original solution
N, M = list(map(int, input().split()))
edges = [list(map(int, input().split())) for _ in range(M)]

graph = [[0] * N for _ in range(N)]
for a, b in edges:
    graph[a - 1][b - 1] = 1
    graph[b - 1][a - 1] = 1


def dfs(v):
    visited[v] = 1
    for v2 in range(N):
        if graph[v][v2]==0 or visited[v2]==1:#vとv2が隣接していない or すでに訪れている場合
            continue
        dfs(v2)#隣接していて、まだ訪れていない場合


ans = 0
for a, b in edges:
    graph[a - 1][b - 1] = 0
    graph[b - 1][a - 1] = 0
    visited = [0] * N

    dfs(0)

    bridge = 0
    if sum(visited) < N :
        bridge = 1
        ans+=1
        
    graph[a - 1][b - 1] = 1
    graph[b - 1][a - 1] = 1

print(ans)

# Optimized solution
N,M=list(map(int,input().split()))
ab=list(list(map(int,input().split())) for _ in range(M))
graph=list([0]*N for _ in range(N))
ans=0
for a,b in ab:
    graph[a - 1][b - 1] = 1
    graph[b - 1][a - 1] = 1

def dfs(s):
    visited[s]=1
    for i in range(N):
        if graph[s][i] == 0 or visited[i] == 1:
            continue
        dfs(i)

for a,b in ab:
    graph[a - 1][b - 1] = 0
    graph[b - 1][a - 1] = 0
    visited=[0]*N
    dfs(0)
    graph[a - 1][b - 1] = 1
    graph[b - 1][a - 1] = 1
    if sum(visited)<N:
        ans+=1


print(ans)
# Original solution
N, M = list(map(int, input().split()))
X = [list(map(int, input().split())) for _ in range(M)]

def find(x):
    if par[x] == x:
        return x
    else:
        par[x] = find(par[x])
        return par[x]

def unite(x, y):
    x = find(x)
    y = find(y)
    if x == y:
        return
    
    if rank[x] < rank[y]:
        par[x] = y
    else:
        par[y] = x
        if rank[x] == rank[y]:
            rank[x] += 1

cnt = 0
for i in range(M):
    par = list(range(N + 1))
    rank = [0] * (N + 1)
    for j, (u, v) in enumerate(X):
        if i == j:
            continue
        unite(u, v)
    
    res = True
    for j in range(1, N + 1):
        for k in range(1, N + 1):
            res &= find(j) == find(k)

    cnt += int(not res)
        
print(cnt)


# Optimized solution
N, M = list(map(int, input().split()))
X = [list(map(int, input().split())) for _ in range(M)]

graph = [[False] * N for _ in range(N)]
for a, b in X:
    graph[a - 1][b - 1] = True
    graph[b - 1][a - 1] = True


def dfs(u):
    visited[u] = True
    for v in range(N):
        if visited[v] or not graph[u][v]:
            continue
        dfs(v)


res = 0
for i in range(M):
    a, b = X[i]
    a -= 1
    b -= 1
    
    graph[a][b] = False
    graph[b][a] = False
    
    visited = [False] * N
    dfs(0)
    res += int(not all(visited))
    
    graph[a][b] = True
    graph[b][a] = True
    
print(res)
# Original solution
import sys
input=sys.stdin.readline
import math

def get_edges_include_node(i, Edges): # 頂点 i を含む辺のリストを返す
    es = []
    for e in Edges:
        if i in e:
            es.append(e)
    return es

def dfs(i, e0, goal, Edges): # 頂点 i から頂点 j にたどり着けるかどうか
    if e0 in Edges:
        Edges.remove(e0)
    for e1 in get_edges_include_node(i, Edges):
        if goal in e1:
            return True
        else:
            ni = e1[1] if e1[0] == i else e1[0]
            if dfs(ni, e1, goal, Edges):
                return True                
    return False
def main():

    N,M = list(map(int, input().split()))
    Edges = []
    for _ in range(M):
        a,b = list(map(int, input().split()))
        Edges.append((a,b))
    ans = 0
    for e in Edges:
        tmp = []
        for e2 in Edges:
            tmp.append((e2[0], e2[1]))
        if not dfs(e[0], e, e[1], tmp):
            ans += 1
    print(ans)


if __name__ == '__main__':
    main()


# Optimized solution
import sys
input = sys.stdin.readline


class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)
        if x == y:
            return
        if self.parents[x] > self.parents[y]:
            x, y = y, x
        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
        return -self.parents[self.find(x)]


def main():
    N, M = list(map(int, input().split()))
    E = []
    for i in range(M):
        a, b = list(map(int, input().split()))
        E.append((a-1, b-1))

    ans = 0
    # i 番目の辺を取り除いても、全ての頂点が連結しているか？
    # Union-Find で連結グラフか判定する
    for i in range(M):
        uf = UnionFind(N) # 最初、全ての頂点が非連結の状態
        for j in range(M):
            if i == j:
                pass # i 番目の辺を無視する
            else:
                # 要素が同じ集合にある＝頂点が連結している
                uf.union(E[j][0], E[j][1])
        # グラフ全体が連結なら、任意の頂点を含む集合のサイズが N のはず
        if uf.size(0) != N:
            ans += 1

    print(ans)


if __name__ == '__main__':
    main()
# Original solution
# ABC075C - Bridge
# find the number of bridges in a given undirected connected graph
# bridge := an edge whose removal disconnects a graph
import sys
from copy import deepcopy

input = sys.stdin.readline
sys.setrecursionlimit(10 ** 9)


def dfs(v: int) -> None:
    seen[v] = 1  # make vertex v searched
    for u in g[v]:
        if not seen[u]:
            dfs(u)


def main():  # O(M(N + M))
    global g, seen
    N, M = tuple(map(int, input().split()))
    E = tuple(tuple(map(int, input().split())) for _ in range(M))
    G = [[] for _ in range(N + 1)]  # N+1: 1-idx
    for v, u in E:  # construct a undirected graph
        G[v] += [u]
        G[u] += [v]
    bridges = 0
    for v, u in E:
        g = deepcopy(G)  # subgraph of G except E(v, u)
        g[v].remove(u)
        g[u].remove(v)
        seen = [0] * (N + 1)
        dfs(1)  # search from vertex 1
        bridges += 1 if 0 in seen[1:] else 0
    print(bridges)


if __name__ == "__main__":
    main()

# Optimized solution
# ABC075C - Bridge
# find the number of bridges in a given undirected connected graph
# bridge := an edge whose removal disconnects a graph
def dfs(start: int) -> None:
    seen[start] = 1  # make vertex v searched
    stack = [start]
    while stack:
        v = stack.pop()
        for u in g[v]:
            if not seen[u]:
                seen[u] = 1
                stack.append(u)


def main():  # O(M(N + M))
    global g, seen
    N, M, *E = list(map(int, open(0).read().split()))
    G = [[] for _ in range(N + 1)]
    for v, u in zip(*[iter(E)] * 2):
        G[v].append(u), G[u].append(v)
    bridges = 0
    for v, u in zip(*[iter(E)] * 2):
        g = [v.copy() for v in G]  # subgraph of G except E(v, u)
        g[v].remove(u), g[u].remove(v)
        seen = [0] * (N + 1)
        dfs(1)  # search from vertex 1
        bridges += 1 if 0 in seen[1:] else 0
    print(bridges)


if __name__ == "__main__":
    main()
# Original solution
from collections import deque

n,m=list(map(int,input().split()))
l=[list(map(int,input().split())) for i in range(m)]

def make_dict(x,y):
    for i in ll:
        if i[x] in list(d.keys()):
            d[i[x]]+=[i[y]]
        else:
            d[i[x]]=[i[y]]

cnt=0
for i in range(m):
    ll=l[:i]+l[i+1:]
#    print(ll)
    d={}
    make_dict(0,1)
    make_dict(1,0)
#    print(d)
    k_l=list(d.keys())
    v_l=list(d.values())
#    print(k_l)

    x=0
    sk=k_l[x]
    dq=deque()
    dq.append(sk)
#    while dq:
#    print(dq)
#    x=0
    seen=[]
    while dq:
#    while n<10:
        k=dq.pop()
        k_ll=d[k]
        seen.append(k)
#        print(k_ll)
        for i in k_ll:
            if i not in seen:
                dq.append(i)
#        d=d.pop(sk)
#        print(dq)
        x+=1
#    print(set(seen),len(set(seen)))
    if len(set(seen))!=n:
        cnt+=1
print(cnt)

# Optimized solution
from collections import deque

n,m=list(map(int,input().split()))
l=[list(map(int,input().split())) for i in range(m)]

def make_dict(x,y):
    for i in ll:
        if i[x] in list(d.keys()):
            d[i[x]]+=[i[y]]
        else:
            d[i[x]]=[i[y]]

cnt=0
for i in range(m):
    ll=l[:i]+l[i+1:]
    d={}
    make_dict(0,1)
    make_dict(1,0)
    k_l=list(d.keys())
    x=0
    sk=k_l[x]
    dq=deque()
    dq.append(sk)
    seen=[]
    while dq:
        k=dq.pop()
        k_ll=d[k]
        seen.append(k)
        for i in k_ll:
            if i not in seen:
                dq.append(i)
        x+=1
    if len(set(seen))!=n:
        cnt+=1
print(cnt)
# Original solution
from collections import deque
from copy import deepcopy

n,m=list(map(int,input().split()))
l=[list(map(int,input().split())) for i in range(m)]

def BFS(m):
  t_l=deepcopy(l)
  del t_l[m]

  tree=[[] for j in range(n)]
  for a,b in t_l:
    a-=1
    b-=1
    tree[a].append(b)
    tree[b].append(a)

  dist=[-1 for i in range(n)]
  dist[0]=0

  que=deque()
  que.append(0)

  while que:
    x=que.popleft()
    for i in tree[x]:
      if dist[i]==-1:
        que.append(i)
        dist[i]=dist[x]+1

  global ans
  if -1 in dist:
    ans+=1

ans=0
for i in range(m):
  BFS(i)

print(ans)

# Optimized solution
from collections import deque
from copy import deepcopy

n,m=list(map(int,input().split()))
l=[list(map(int,input().split())) for i in range(m)]

def DFS(m):
  t_l=deepcopy(l)
  del t_l[m]

  tree=[[] for j in range(n)]
  for a,b in t_l:
    a-=1
    b-=1
    tree[a].append(b)
    tree[b].append(a)

  seen=set()
  seen.add(0)

  dq=deque()
  dq.append(0)

  while dq:
    x=dq.pop()
    for i in tree[x]:
      if i not in seen:
        dq.append(i)
        seen.add(i)

  return seen

ans=0
for i in range(m):
  st=DFS(i)
  if len(st)!=n:
    ans+=1

print(ans)
# Original solution
import sys

sys.setrecursionlimit(10**7)
def I(): return int(sys.stdin.readline().rstrip())
def MI(): return list(map(int,sys.stdin.readline().rstrip().split()))
def LI(): return list(map(int,sys.stdin.readline().rstrip().split()))  #空白あり
def LI2(): return list(map(int,sys.stdin.readline().rstrip()))  #空白なし
def S(): return sys.stdin.readline().rstrip()
def LS(): return list(sys.stdin.readline().rstrip().split())  #空白あり
def LS2(): return list(sys.stdin.readline().rstrip())  #空白なし


class UnionFind:
    def __init__(self,n):
        self.par = [i for i in range(n+1)]  # 親のノード番号
        self.rank = [0]*(n+1)
    def find(self,x):  # xの根のノード番号
        if self.par[x] == x:
            return x
        else:
            self.par[x] = self.find(self.par[x])
            return self.par[x]
    def same_check(self,x,y):  # x,yが同じグループか否か
        return self.find(x) == self.find(y)
    def unite(self,x,y):  # x,yの属するグループの併合
        x = self.find(x)
        y = self.find(y)
        if self.rank[x] < self.rank[y]:
            x,y = y,x
        if self.rank[x] == self.rank[y]:
            self.rank[x] += 1
        self.par[y] = x


N,M = MI()
edges = [tuple(MI()) for _ in range(M)]

ans = 0
for i in range(M):
    U = UnionFind(N)
    for j in range(M):
        if j != i:
            a,b = edges[j]
            U.unite(a,b)
    x,y = edges[i]
    if not U.same_check(x,y):
        ans += 1

print(ans)


# Optimized solution
import sys
def MI(): return list(map(int,sys.stdin.readline().rstrip().split()))


class UnionFind:
    def __init__(self,n):
        self.par = [i for i in range(n+1)]  # 親のノード番号
        self.rank = [0]*(n+1)
    def find(self,x):  # xの根のノード番号
        if self.par[x] == x:
            return x
        else:
            self.par[x] = self.find(self.par[x])
            return self.par[x]
    def same_check(self,x,y):  # x,yが同じグループか否か
        return self.find(x) == self.find(y)
    def unite(self,x,y):  # x,yの属するグループの併合
        x = self.find(x)
        y = self.find(y)
        if self.rank[x] < self.rank[y]:
            x,y = y,x
        if self.rank[x] == self.rank[y]:
            self.rank[x] += 1
        self.par[y] = x


N,M = MI()
edges = [tuple(MI()) for _ in range(M)]

ans = 0
for i in range(M):
    U = UnionFind(N)
    for j in range(M):
        if j != i:
            a,b = edges[j]
            U.unite(a,b)
    x,y = edges[i]
    if not U.same_check(x,y):
        ans += 1

print(ans)