#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
using namespace std;
#ifdef ENABLE_DEBUG
#define dump(a) cerr<<#a<<"="<<a<<endl
#define dumparr(a,n) cerr<<#a<<"["<<n<<"]="<<a[n]<<endl
#else
#define dump(a) 
#define dumparr(a,n) 
#endif
#define FOR(i, a, b) for(ll i = a;i < b;i++)
#define For(i, a) FOR(i, 0, a)
#define REV(i, a, b) for(ll i = b-1;i >= a;i--)
#define Rev(i, a) REV(i, 0, a)
#define REP(a) For(i, a)

typedef long long int ll;
typedef unsigned long long ull;
typedef unsigned int uint;
typedef pair<ll, ll> pll;
typedef pair<ll,pll> ppll;
typedef vector<ll> vll;

const ll INF=(1LL<<50);
#if __cplusplus>=201700L
ll gcd(ll a, ll b) {
  if(a < b) return gcd(b, a);
  ll r;
  while ((r=a%b)) {
    a = b;
    b = r;
  }
  return b;
}
#endif

template<class S,class T>
std::ostream& operator<<(std::ostream& os,pair<S,T> a){
  os << "(" << a.first << "," << a.second << ")";
  return os;
}
template<class T>
std::ostream& operator<<(std::ostream& os,vector<T> a){
  os << "[ ";
  REP(a.size()){
    os<< a[i] << " ";
  }
  os<< " ]";
  return os;
}
struct max_flow {
    struct edge { ll to, cap, rev; };
    ll V;
    vector<vector<edge>> G;
    vector<ll> itr, level;

    max_flow(ll V) : V(V) { G.assign(V,vector<edge>()); }

    void add_edge(ll from, ll to, ll cap) {
        G[from].push_back((edge) {to, cap, (ll) G[to].size()});
        G[to].push_back((edge) {from, 0, (ll) G[from].size()-1});
    }

    void bfs(ll s) {
        level.assign(V,-1);
        queue<ll> q;
        level[s] = 0; q.push(s);
        while (!q.empty()) {
            ll v = q.front(); q.pop();
            for(auto &e: G[v]){
                if (e.cap > 0 and level[e.to] < 0) {
                    level[e.to] = level[v] + 1;
                    q.push(e.to);
                }
            }
        }
    }

    ll dfs(ll v, ll t, ll f) {
        if (v == t) return f;
        for (ll& i = itr[v]; i < (ll) G[v].size(); ++i) {
            edge& e = G[v][i];
            if (e.cap > 0 and level[v] < level[e.to]) {
                ll 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;
    }

    ll run(ll s, ll t) {
        ll ret = 0, f;
        while (bfs(s), level[t] >= 0) {
            itr.assign(V,0);
            while ((f = dfs(s, t, INF)) > 0) ret += f;
        }
        return ret;
    }
};


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){
  auto flow = max_flow(2*N+2);
  REP(N){
    flow.add_edge(2*N,i,1);
    flow.add_edge(N+i,2*N+1,1);
    For(j,N){
      if(a[i]<c[j]&&b[i]<d[j]){
        flow.add_edge(i,N+j,1);
      }
    }
  }
  cout<<flow.run(2*N,2*N+1)<<endl;
}

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: 512