#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>
#include <array>
#include <vector>
#include <deque>
#include <list>
#include <set>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <stack>
#include <queue>
#include <bitset>
#include <tuple>
#include <cmath>
#include <complex>
#include <algorithm>
#include <utility>
#include <regex>
#include <cstdint>
#include <numeric>
#include <functional>
#include <cassert>

using namespace std;
using ll = int64_t;
using ull = uint64_t;

constexpr ll INF = 1000000000;/* 1e+9a */
using index_type  = ll;
using V_set = std::unordered_set<index_type>;
using E_set = std::unordered_map<index_type, V_set>;

class bipartite_graph {
    // bipartite graph : V -> (V1, V2);
    std::unordered_map<index_type, V_set> LtoR;
    std::unordered_map<index_type, index_type> PairL, PairR;
    std::unordered_map<index_type, index_type> Dist;
    const index_type NIL = 10000000000;
    static constexpr index_type INF = std::numeric_limits<index_type>::max();

    index_type L, R;

    bool BFS();
    bool DFS(index_type l);

    std::stack<V_set> removed_edge;
    std::stack<index_type> removed_v;
public:
    bipartite_graph(index_type l, index_type r, V_set v_num);
    bipartite_graph(index_type l, index_type r, V_set v_numL, V_set v_numR);

    void add_edge(index_type l, index_type r) {
        assert(name_of_verticesR.count(r) > 0);
        assert(name_of_verticesL.count(l) > 0);
        LtoR[l].insert(r);
    };

    void remove_vertex(index_type v);

    void restore_vertex();

    std::pair<V_set, V_set> create_VC();

    void add_vertex(index_type v);

    index_type Hopcroft_Karp(); // return maximum matching size

    // return minimum vertex cover S in {L x R}
    // this function must use after Hopcroft_Karp function
    // each return vector is sorted
    std::pair<V_set, V_set> Konig();
    V_set name_of_verticesL, name_of_verticesR;
};


void sub_main(istream &cin) {
    index_type N;
    cin >> N;
    if(!cin) return;
    V_set num;
    vector<pair<ll, ll>> a, b;
    for (int i = 0; i < N; ++i) {
        num.insert(i);
        int ta, tb;
        cin >> ta >> tb;
        a.push_back({ta, tb});
    } // end i
    for (int i = 0; i < N; ++i) {
        int ta, tb;
        cin >> ta >> tb;
        b.push_back({ta, tb});
    } // end i
    bipartite_graph bg(N, N, num);
    for (int i = 0; i < N; ++i) {
        auto pa = a[i];
        for (int j = 0; j < N; ++j) {
            auto pb = b[j];
            if(pa.first < pb.first and pa.second < pb.second){
                bg.add_edge(i, j);
            }
        } // end pb
    } // end pa
    cout << bg.Hopcroft_Karp() << endl;
}

int main(int argc, char *argv[]) {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    cout << setprecision(16) << scientific;
#ifdef DEBUG
    cerr << "DEBUG MODE" << endl;
    string test_cases = "test.txt";
    auto fs = fstream(test_cases, fstream::in);
    while(fs) {
        sub_main(fs);
    }
#else
    sub_main(cin);
#endif
    return 0;
}
index_type bipartite_graph::Hopcroft_Karp() {
    PairR.clear();
    PairL.clear();
    for (auto &&i : name_of_verticesR) { PairR[i] = NIL; } // end i
    for (auto &&i : name_of_verticesL) { PairL[i] = NIL; } // end i
    index_type match = 0;
    while (BFS()) {
        for (auto &&i : name_of_verticesL) {
            if (PairL[i] == NIL and DFS(i)) { match++; }
        }
    }
    return match;
}

bool bipartite_graph::BFS() {
    std::queue<index_type> lque;
    for (auto &&l : name_of_verticesL) {
        if (PairL[l] == NIL) {
            Dist[l] = 0;
            lque.push(l);
        } else {
            Dist[l] = INF;
        }
    } // end l
    Dist[NIL] = INF;
    auto &&nilD = Dist[NIL];
    while (!lque.empty()) {
        auto l = lque.front();
        lque.pop();
        if (Dist[l] < nilD) {
            for (auto &&r : LtoR[l]) {
                auto &&pr = PairR[r];
                if (Dist[pr] == INF) {
                    Dist[pr] = Dist[l] + 1;
                    lque.push(pr);
                }
            } // end r
        }
    }
    return nilD != INF;
}

bool bipartite_graph::DFS(index_type l) {
    if (l == NIL) return true;
    for (auto &&r : LtoR[l]) {
        auto &&pr = PairR[r];
        if (Dist[pr] == Dist[l] + 1)
            if (DFS(pr)) {
                pr = l;
                PairL[l] = r;
                return true;
            }
    } // end v
    Dist[l] = INF;
    return false;
}

std::pair<V_set, V_set> bipartite_graph::Konig() {
    // PairL, PairR : maximum matching
    std::unordered_map<index_type, bool> VCL, VCR;
    for (auto &&i : name_of_verticesL) { VCL[i] = true; }
    for (auto &&i : name_of_verticesR) { VCR[i] = false; } // end i
    std::queue<index_type> lque;
    // L内のunmatched vertexをlqueに追加
    // lqueからalternative pathでたどり着く頂点探索
    // Lの頂点なら不採用，Rの頂点は採用

    for (auto &&i : name_of_verticesL) {
        if (PairL[i] == NIL) {
            VCL[i] = false;
            for (auto &&r : LtoR[i]) {
                if (PairR[r] != NIL) {
                    VCR[r] = true;
                    lque.push(PairR[r]);
                }
            } // end r
        }
    } // end i

    while (!lque.empty()) {
        auto l = lque.front();
        lque.pop();
        if (VCL[l]) {
            VCL[l] = false;
            for (auto &&r : LtoR[l]) {
                if (PairR[r] == l) continue;
                VCR[r] = true;
                if (PairR[r] != NIL) lque.push(PairR[r]);
            } // end r
        }
    }

    V_set ansL, ansR;
    for (auto &&i : VCL) {
        if (i.second) ansL.insert(i.first);
    } // end i
    for (auto &&i : VCR) {
        if (i.second) ansR.insert(i.first);
    } // end i

    return {ansL, ansR};
}

void bipartite_graph::remove_vertex(index_type v) {
    // ```for all v, u, u in LtoR[v] iff v in LtoR[u]```が成立を仮定
    removed_v.push(v);
    removed_edge.push(LtoR[v]);

    for (auto &&p : LtoR[v]) {
        LtoR[p].erase(v);
    } // end p
    LtoR.erase(v);
    name_of_verticesL.erase(v);
}

void bipartite_graph::restore_vertex() {
    while (!removed_v.empty()) {
        auto v = removed_v.top();
        removed_v.pop();
        auto e = removed_edge.top();
        removed_edge.pop();

        name_of_verticesL.insert(v);
        LtoR[v] = e;
        for (auto &&p : LtoR[v]) {
            LtoR[p].insert(v);
        } // end p
    }
}

void bipartite_graph::add_vertex(index_type v) {
    name_of_verticesL.insert(v);
}

bipartite_graph::bipartite_graph(index_type l, index_type r, V_set v_num)
        : LtoR(l), PairL(l), PairR(r), Dist(l), L(l), R(r),
          removed_v(), name_of_verticesL(v_num), name_of_verticesR(v_num) {}
bipartite_graph::bipartite_graph(index_type l, index_type r, V_set v_numL, V_set v_numR)
        : LtoR(l), PairL(l), PairR(r), Dist(l), L(l), R(r),
          removed_v(), name_of_verticesL(std::move(v_numL)), name_of_verticesR(std::move(v_numR)) {}



Measured memory_bytes: 384