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

typedef long long ll; // -9,223,372,036,854,775,808 から 9,223,372,036,854,775,807

template <class T> using vec = std::vector<T>;
template <class T> using vec2 = vec<vec<T>>;
template <class T> using vec3 = vec<vec<vec<T>>>;

// #define DEBUG

struct Edge{
    int to;
    ll cap;
    shared_ptr<Edge> rev;
};

ll dfs(int s, int t, ll ub, vec2<shared_ptr<Edge>>& edges, vec<bool>& visited){
    if(s == t){
        return ub;
    }
    
    #ifdef DEBUG
    cout << s << endl;
    #endif

    visited.at(s) = true;

    for(auto iter: edges.at(s)){
        if(visited.at(iter->to)){
            continue;
        }
        if(iter->cap == 0){
            continue;
        }
        ll subRes = dfs(iter->to, t, min(ub, iter->cap), edges, visited);
        if(subRes > 0){
            // そこにながす
            iter->cap -= subRes;
            iter->rev->cap += subRes;
            assert(iter->cap == 0);
            assert(iter->rev->cap == 1);
            return subRes;
        }
    }

    return 0;
}

void addEdge(int from, int to, vec2<shared_ptr<Edge>>& edges){
    auto e1 = shared_ptr<Edge>(new Edge{to, 1, NULL});
    auto e2 = shared_ptr<Edge>(new Edge{from, 0, e1});
    e1->rev = e2;
    edges.at(from).push_back(e1);
    edges.at(to).push_back(e2);
    assert(e1->rev == e2);
    assert(e2->rev == e1);
    #ifdef DEBUG
    printf("Edge: from %d to %d\n", from, to);
    #endif
}

void solve(long long N, std::vector<long long> a, std::vector<long long> b, std::vector<long long> c, std::vector<long long> d){
    vec<complex<ll>> reds;
    vec<complex<ll>> blues;
    for(int i=0; i < N; i++){
        reds.push_back(complex<ll>(a.at(i), b.at(i)));
        blues.push_back(complex<ll>(c.at(i), d.at(i)));

    }

    int startingRed = 0;
    int startingBlue = startingRed + N;
    int s = startingBlue + N;
    int t = s + 1;
    int numV = t + 1;

    // bipartite graphをつくる
    vec2<shared_ptr<Edge>> edges(numV);
    // まず点間をつくる
    for(int i=0; i < N; i++){
        for(int j=0; j < N; j++){
            if(reds.at(i).real() < blues.at(j).real() 
            && reds.at(i).imag() < blues.at(j).imag()){
                addEdge(startingRed + i, startingBlue + j, edges);
            }
        }
    }

    // 次にスタートからredをつくる
    for(int i=0; i < N; i++){
        addEdge(s, startingRed + i, edges);
    }

    // blueからgoalをつくる
    for(int i=0; i < N; i++){
        addEdge(startingBlue + i, t, edges);;

    }

    // フローを計算
    ll res = 0;
    while(true){
        vec<bool> visited(numV);
        ll subRes = dfs(s, t, edges.size(), edges, visited);
        res += subRes;
        #ifdef DEBUG
        printf("*START: subres\n");
        cout << subRes << endl;
        printf("*END: subres\n");
        #endif
        if(subRes == 0){
            break;
        }
    }

    cout << res << endl;

}

// Generated by 1.1.6 https://github.com/kyuridenamida/atcoder-tools  (tips: You use the default template now. You can remove this line by using your custom template)
int main(){
    long long N;
    scanf("%lld",&N);
    std::vector<long long> a(N);
    std::vector<long long> b(N);
    for(int i = 0 ; i < N ; i++){
        scanf("%lld",&a[i]);
        scanf("%lld",&b[i]);
    }
    std::vector<long long> c(N);
    std::vector<long long> d(N);
    for(int i = 0 ; i < N ; i++){
        scanf("%lld",&c[i]);
        scanf("%lld",&d[i]);
    }
    solve(N, std::move(a), std::move(b), std::move(c), std::move(d));
    return 0;
}


Measured memory_bytes: 896