import std.stdio, std.algorithm, std.string, std.conv, std.array, std.range, std.math;
int readint() { return readln.chomp.to!int; }
int[] readints() { return readln.split.to!(int[]); }

immutable int INF = 114514810;

// ari 196
struct Edge {
    int to, cap, rev;
}
Edge[][] G;
bool[] used; // dfs したか

// 増加パスを探す
int dfs(int v, int t, int f) {
    if (v == t) return f;
    used[v] = true;
    foreach (ref e; G[v]) {
        if (!used[e.to] && e.cap > 0) {
            int d = dfs(e.to, t, min(f, e.cap));
            if (d > 0) {
                e.cap -= d;
                G[e.to][e.rev].cap += d;
                return d;
            }
        }
    }
    return 0;
}

immutable int VMAX = 300;
int max_flow(int s, int t) {
    int flow;
    used.length = VMAX;
    while (1) {
        fill(used, false);
        int f = dfs(s, t, INF);
        if (f == 0) return flow;
        flow += f;
    }
    assert(false);
}

void add_edge(int from, int to, int cap) {
    G[from] ~= Edge(to, cap, G[to].length.to!int);
    G[to]   ~= Edge(from, 0, G[from].length.to!int - 1);
}

struct Point { int x, y; }

void main() {
    int n = readint();
    G.length = VMAX;
    auto ps = new Point[2 * n];
    foreach (ref p ; ps) readf("%d %d\n", &p.x, &p.y);

    int s = 2 * n, t = s + 1;
    foreach (i; 0..n) {
        add_edge(s, i, 1);
        add_edge(n + i, t, 1);
    }
    foreach (i; 0..n) foreach (j; 0..n) {
        if (ps[i].x < ps[n + j].x && ps[i].y < ps[n + j].y)
            add_edge(i, n + j, 1);
    }

    writeln(max_flow(s, t));
}