//#pragma GCC target ("avx2")
//#pragma GCC optimization ("O3")
//#pragma GCC optimization ("unroll-loops")
#include<bits/stdc++.h>
//#include <ext/pb_ds/assoc_container.hpp>
//#include <ext/pb_ds/tree_policy.hpp>
//using namespace __gnu_pbds;
#define eps 1e-8
//#define eq(x,y) (fabs((x)-(y)) < eps)
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<int,int>pii;
const int mod= 1e9+7;
long double pi = acosl(-1);
//const ll infl = LLONG_MAX;
const int inf = 1e9+2;
const int nmax = 4e5+5;
//const int MAXLG = log2(nmax)+1;
//mt19937 rng(chrono::system_clock::now().time_since_epoch().count());
//typedef tree< int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update> ost;

typedef long long LL;

struct Edge {
    int u, v;
    LL cap, flow;
    Edge() {}
    Edge(int u, int v, LL cap): u(u), v(v), cap(cap), flow(0) {}
};

struct Dinic {
    int N;
    vector<Edge> edges;
    vector<vector<int>> adj;
    vector<int> d, pt;

    Dinic(int N): N(N), edges(0), adj(N), d(N), pt(N) {}

    void AddEdge(int u, int v, LL cap) {
        if (u != v) {
            edges.emplace_back(u, v, cap);
            adj[u].emplace_back(edges.size() - 1);
            edges.emplace_back(v, u, 0);
            adj[v].emplace_back(edges.size() - 1);
        }
    }

    bool BFS(int S, int T) {
        queue<int> q({S});
        fill(d.begin(), d.end(), N + 1);
        d[S] = 0;
        while(!q.empty()) {
            int u = q.front(); q.pop();
            if (u == T) break;
            for (int k: adj[u]) {
                Edge &e = edges[k];
                if (e.flow < e.cap && d[e.v] > d[e.u] + 1) {
                    d[e.v] = d[e.u] + 1;
                    q.emplace(e.v);
                }
            }
        }
        return d[T] != N + 1;
    }

    LL DFS(int u, int T, LL flow = -1) {
        if (u == T || flow == 0) return flow;
        for (int &i = pt[u]; i < adj[u].size(); ++i) {
            Edge &e = edges[adj[u][i]];
            Edge &oe = edges[adj[u][i]^1];
            if (d[e.v] == d[e.u] + 1) {
                LL amt = e.cap - e.flow;
                if (flow != -1 && amt > flow) amt = flow;
                if (LL pushed = DFS(e.v, T, amt)) {
                    e.flow += pushed;
                    oe.flow -= pushed;
                    return pushed;
                }
            }
        }
        return 0;
    }

    LL MaxFlow(int S, int T) {
        LL total = 0;
        while (BFS(S, T)) {
            fill(pt.begin(), pt.end(), 0);
            while (LL flow = DFS(S, T))
                total += flow;
        }
        return total;
    }
};


int main(){
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);

    int n;
    cin>>n;
    vector<pii>v(n);
    for(pii &x : v) cin>>x.first>>x.second;
    vector<int>shob;
    for(pii z : v) shob.push_back(z.first), shob.push_back(z.second);
    sort(shob.begin(), shob.end());
    shob.erase(unique(shob.begin(), shob.end()), shob.end());
    for(pii &z : v){
        z.first = lower_bound(shob.begin(), shob.end(), z.first) - shob.begin();
        z.second = lower_bound(shob.begin(), shob.end(), z.second) - shob.begin();
    }
    int sz = shob.size();
    int N = sz + n + 2;
    int sorc = N-2, sink = N-1;
    Dinic flu(N);
    for(int i=0; i<n; i++){
        flu.AddEdge(sorc, sz+i, 1);
        flu.AddEdge(sz+i, v[i].first, 1);
        flu.AddEdge(sz+i, v[i].second, 1);
    }
    for(int i=0; i<sz; i++){
        flu.AddEdge(i, sink, 1);
    }
    cout<<flu.MaxFlow(sorc, sink)<<endl;
}
/*
*/

#include <bits/stdc++.h>
using namespace std;

#ifdef Dhiraj
    #include "D:/dhiraj/Programming/debug.h"
#else
    #define d(...) 11
    #define cerr if(0) cerr
#endif

#define ll long long int
#define endl '\n'

template <typename T, typename U>
inline istream& operator >> (istream& in, pair<T, U>& p) {
    in >> p.first >> p.second;
    return in;
}
template <typename T>
inline istream& operator >> (istream& in, vector<T>& v) {
    for(T& x : v) in >> x;
    return in;
}

const ll MX = 4e5 + 5;

ll n, m;
vector<vector<ll>> g;
vector<ll> vis;

void clearAll()
{
    vis.assign(MX, 0);
    g.assign(MX, vector<ll>());
}

ll cc, edges;
void dfs(ll u)
{
    if(vis[u] || g[u].empty()) return;
    cc++;
    vis[u] = 1;
    for(ll v : g[u]) {
        edges++;
        dfs(v);
    }
}

void solve(int &tc)
{
    cin >> n;
    clearAll();
    
    for(ll i = 0; i < n; i++)
    {
        ll x, y;
        cin >> x >> y;
        g[x].emplace_back(y);
        g[y].emplace_back(x);
    }

    ll ans = 0;
    for(ll i = 0; i < MX; i++)
    {
        cc = edges = 0;
        dfs(i);
        edges /= 2;
        ans += min(cc, edges);
    }
    cout << ans << endl;
}

int main()
{
    #ifdef Dhiraj
        freopen("D:/dhiraj/Programming/i1.txt", "r", stdin);
        freopen("D:/dhiraj/Programming/o1.txt", "w", stdout);
        // freopen("D:/dhiraj/Programming/e1.txt", "w", stderr);
    #endif

    ios::sync_with_stdio(false);
    cin.tie(0);

    int tc = 1;
    for(int i = 1; i <= tc; i++) {
        cerr << "Case #" << i << "\n";
        solve(i);
    }
    return 0;
}