#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100010, M = N * 2;
//n实际输入的结点数
int n;
//h[N] 存储n个单链表,有n个头结点,每个结点一个单链表,指向它连通的结点。初始化为-1,表示指向空结点
//e[M] 存储每个结点编号,N表示的是下标,M开两倍的N, 原因是无向边,每个点最多会被两个节点指着,等于边数*2 = (n-1)*2
//ne[M] 存储每个结点的下一个结点
//idx 表示可以用的结点下标
int h[N], e[M], ne[M], idx;
bool st[N]; //每个结点是否被遍历过,初始化为false
int ans = N; //存储最小的剩余各个连通块中点数的最大值,最终答案
//dfs有一个特点,可以附加的带回这个子树有多少个节点
//返回以u为根的子树中点的数量
int dfs(int u)
{
st[u] = true;
int sum = 1, res = 0;
//sum 表示子树中点的数量,加上自己的1,与下方的max(res, n - sum)有关
//res是各个连通块中最大值,与下方的 max(res, s)有关
for (int i = h[u]; i != -1; i = ne[i])
{
int j = e[i];
if (!st[j])
{
int s = dfs(j);//返回以j为根的子树中点的个数
res = max(res, s);//更新运行res
sum += s; //更新以u为根结点子树点的数量(包含自己)
}
}
res = max(res, n - sum);//子树中的连通块已经算完, 还有剩余的那一块,总结点个数-子树结点点个数-自身个数,更新res
ans = min(ans, res);//ans记录最小的res
return sum;//dfs递归结束,返回以u为根的子树中点的数量
}
void add(int a, int b)//把下标为idx,编号为b的结点插入编号为a的头结点后面
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;//头插
}
int main()
{
cin >> n;
memset(h, -1, sizeof h);//初始化,每个点指向空结点
for (int i = 0; i < n - 1; i ++ )//输入n - 1条边
{
int a, b;
cin >> a >> b;
add(a, b), add(b, a);//每条边两个点,无向边。
}
dfs(n); //dfs(1..n)都可以,dfs(1),以1为根结点,dfs(n)以n为根结点
cout << ans << endl;
return 0;
}