#include <bits/stdc++.h>
#include <boost/range/irange.hpp>
#include <boost/range/adaptors.hpp>

using namespace std;
using namespace boost::adaptors;

using ll = long long;

//    フォードファルカーソン法では，ただひたすら貪欲に流していくだけ
//    DFSで容量がゼロでないものについて，見ていく
//    vector<N> visitedを持っておく
//    流れ着いたら，使った辺の容量を減らし，逆辺の容量を増やす
//    ただし，スタックからのロールバックの時に，コールバックが必要
//    tにたどり着いたらコールバックを行う
//    コールバックでも
//    vector<variable> prev :(ある深さで訪れたノード, その時に流そうとした)　を持っておいて，pushの時に，自分の深さを記録していく
struct Edge {
    ll cap, to, rev;

    explicit Edge(ll cap, ll to, ll rev) : cap(cap), to(to), rev(rev) {}
};

const ll Flow(const ll now, const ll terminate, const ll flow_trying, vector<vector<Edge>> &nodes, vector<bool> &used) {
    if (now == terminate) return flow_trying;
    used.at(now) = true;
    for (Edge &e : nodes.at(now)) {
        if (!used.at(e.to) && e.cap > 0) {
            const ll flow_actual = Flow(e.to, terminate, min(flow_trying, e.cap), nodes, used);
            if (flow_actual > 0){
//                string from, to;
//                ll N = (nodes.size() - 2) / 2;
//                auto get_name = [&N](ll index)->string{
//                    char buffer[100];
//                    if (index < N){
//                        sprintf(buffer, "red %lld", index);
//                    } else if (index < 2 * N){
//                        sprintf(buffer, "blue %lld", index - N);
//                    } else if (index == 2 * N){
//                        sprintf(buffer, "start");
//                    } else {
//                        sprintf(buffer, "terminate");
//                    }
//                    return buffer;
//                };
//                from = get_name(now);
//                to = get_name(e.to);
//                cout << to << " <-- " << from << " ";
                e.cap -= flow_actual;
                Edge &reverse_e = nodes.at(e.to).at(e.rev);
                reverse_e.cap += flow_actual;
                return flow_actual;
            }
        }
    }
    return 0; // もし有効なエッジがなかったら，「何も流せなかったよ」と報告する
}


int main() {
    ll N;
    cin >> N;
//    ここでredsとbluesに分けないことがポイント．どうせ後でnodesを作るときにインデクシングに困るだけ．
    vector<pair<ll, ll>> points;
    for (auto &&i: boost::irange(0LL, N)) {
        ll x, y;
        cin >> x >> y;
        points.emplace_back(make_pair(x, y));
    }
    for (auto &&i: boost::irange(0LL, N)) {
        ll x, y;
        cin >> x >> y;
        points.emplace_back(make_pair(x, y));
    }
//    ford-fulkerson method
    vector<vector<Edge>> nodes(2 * N + 2, vector<Edge>{}); // cap, to, rev
//    まず二部グラフを構築する
//    redsからbluesにしか流せない
//    reds: i == nodes[i]
//    blues: i = nodes[N+i]
//    source: nodes[2N]
//    sink: nodes[2N+1]

//    まずはredとblueの関係を貼る
    for (auto &&r: boost::irange(0LL, N)) {
        ll xr, yr;
        tie(xr, yr) = points.at(r);
//        ノードの作成
        for (auto &&b: boost::irange(N, 2 * N)) {
            ll xb, yb;
            tie(xb, yb) = points.at(b);
            if (xr < xb && yr < yb) {
//                cout << "from r: " << r << "to b: " << b-N << endl;
//                この条件が満たされるときにだけ，順辺と逆辺を貼る
//                まだ存在しない逆辺を指定する（激ヤバ）
                nodes.at(r).emplace_back(Edge(1LL, b, (ll) nodes.at(b).size()));
//                こっちはさっき作った辺を指定する
                nodes.at(b).emplace_back(Edge(0LL, r, (ll) nodes.at(r).size() - 1));
            }
        }
    }
//    次に，sourceからredの関係を貼る
    ll s = 2 * N;
    for (auto &&r: boost::irange(0LL, N)) {
        nodes.at(s).emplace_back(Edge(1LL, r, (ll) nodes.at(r).size()));
        nodes.at(r).emplace_back(Edge(0LL, 2 * N, (ll) nodes.at(s).size() - 1));
    }
//    最後にblueからsinkの関係を貼る
    ll t = 2 * N + 1;
    for (auto &&b: boost::irange(N, 2 * N)) {
        nodes.at(b).emplace_back(Edge(1LL, t, (ll) nodes.at(t).size()));
        nodes.at(t).emplace_back(Edge(0LL, b, (ll) nodes.at(b).size()));
    }

//    for (auto &&i: boost::irange(0LL, 2*N+2)){
//        auto get_name = [&N](ll index)->string{
//            char buffer[100];
//            if (index < N){
//                sprintf(buffer, "red %lld", index);
//            } else if (index < 2 * N){
//                sprintf(buffer, "blue %lld", index - N);
//            } else if (index == 2 * N){
//                sprintf(buffer, "start");
//            } else {
//                sprintf(buffer, "terminate");
//            }
//            return buffer;
//        };
//        for (auto &&e: nodes.at(i)){
//            if (e.cap != 0) cout << get_name(i)  << " --> " << get_name(e.to) << endl;
//        }
//    }
//    フォードファルカーソン法では，ただひたすら貪欲に流していくだけ
//    DFSで容量がゼロでないものについて，見ていく
//    vector<N> usedを持っておく
    ll flow{0};
    while (true) {
        vector<bool> used(2 * N + 2, false);
        used.at(s) = true;
        ll f = Flow(s, t, numeric_limits<ll>::max(), nodes, used);
//        cout << endl;
        if (f == 0)break;
        flow += f;
    }
    cout << flow;
}

Measured memory_bytes: 512