#include <iostream>
#include <iomanip>
#include <vector>
#include <algorithm>
#include <cstring>
#include <map>
#include <queue>
#include <cmath>
#include <complex> // complex<double> a(1.2 , 2.3);// real(): 1.2, imag()2.3
using namespace std;

#define MOD 1000000007
#define ll long long
#define ld long double
#define FOR(i,a,b) for(ll i=(ll)a;i<(ll)b;i++)
#define rep(i,n) FOR(i,0,n)
#define pb push_back
#define mp make_pair
#define all(a) a.begin(),a.end()
#define rall(a) a.rbegin(),a.rend()
#define rmsame(a) sort(all(a)),a.erase(unique(all(a)), a.end())
#define rmvector(a,b) rep(i,a.size())rep(j,b.size())if(a[i]==b[j]){a.erase(a.begin()+i);i--;break;}
#define pq_pair_tB priority_queue <pair<ll,ll>, vector<pair<ll,ll> > , greater<pair<ll,ll> > > 
#define pq_pair_ts priority_queue <pair<ll,ll> > //第二成分の大きさが関係ない
template<typename X> bool exist(vector<X> vec, X item){return find(all(vec), item)!=vec.end();}
ll gcd(ll a, ll b){if(b==0)return a;return gcd(b,a%b);}
ll lcm(ll a, ll b){return a/gcd(a,b)*b;}

template <class T> struct Dinic {
    
    struct Edge {
        int to,rev; T cap;
        Edge (int t, int r, T c) : to(t), rev(r), cap(c) {}
    };
    
    vector < vector< Edge > > G;
    vector < int > level, iter;
    const T INF = numeric_limits < T > ::max();
    Dinic (int n) : G(n), level(n, 0), iter(n, 0) {}
    
    void add_edge(int from, int to, T cap) {
        G[from].push_back(Edge(to, (int)(G[to].size()), cap));
        G[to].push_back(Edge(from, (int)(G[from].size()) - 1, 0));
    }
    
    void bfs(int s) {
        fill(level.begin(), level.end(), -1);
        queue < int > que({s});

        level[s] = 0;
        while (!que.empty()) {
            int v = que.front();
            que.pop();
            for (int i = 0; i < G[v].size(); i++) {
                Edge& e = G[v][i];
                if (e.cap > 0 && level[e.to] < 0) {
                    level[e.to] = level[v] + 1;
                    que.push(e.to);
                }
            }
        }
    }
    
    T dfs(int cur, int t, T f) {
        if (cur == t) return f;
        for (int &i = iter[cur]; i < G[cur].size(); i++) {
            Edge& e = G[cur][i];
            if (e.cap > 0 && level[e.to] > level[cur]) {
                T d = dfs(e.to, t, min(f, e.cap));
                if (d == 0) continue;
                e.cap -= d;
                G[e.to][e.rev].cap += d;
                return d;
            }
        }
        return 0;
    }
    
    T max_flow(int s, int t) {
        T flow = 0, d;
        while (true) {
            bfs(s);
            if(level[t] < 0) break;
            fill(iter.begin(), iter.end(), 0);
            while ((d = dfs(s, t, INF)) > 0) {
                flow += d;
            }
        }
        return flow;
    }
    
};
/*
 O(N*N*M)
//Usage
 Dinic<ll> D(N);
 D.add_edge(from, to , cap);
 D.max_flow(s, t);
*/




int main(){
    cin.tie(0);
    ios::sync_with_stdio(false);
    
    ll N; cin >> N ;
    map<ll, pair<ll,ll> > pointr, pointb;
    
    Dinic<int> D(N*2+2);
    //    void add_edge(int from, int to, T cap)
    
    rep(i, N){
        ll a, b ;
        cin >> a >> b;
        pointr[i] = mp(a,b);
    }
    rep(i,N){
        ll a, b;
        cin >> a >> b;
        pointb[i] = mp(a,b);
    }
    
    // from 0 ->   ->   -> N+N+1;
    rep(i, N) D.add_edge((int)0, (int)(i+1), (int)1);
    rep(i, N) D.add_edge((int)(N+i+1), (int)(N+N+2-1), (int)1);
    
    rep(i,N){// red
        rep(j,N){// blue
            if((pointr[i].first < pointb[j].first) && (pointr[i].second < pointb[j].second)){
                D.add_edge((int)(i+1), (int)(j+N+1) , (int)1);
            }
        }
    }
    
    cout << D.max_flow((int)0, (int)(N + N +1)) << endl ;

    //cout << fixed << setprecision(16) << ans << endl;
    return 0;
}


Measured memory_bytes: 384