// Slower C++ solution:
#include<bits/stdc++.h>

#define MAX_V 100000

#define INF 1<<29

using namespace std;

struct edge{int to,cap,rev;};

vector<edge>G[MAX_V];

int level[MAX_V];

int iter[MAX_V];

void add_edge(int from,int to,int cap){

  G[from].push_back((edge){to,cap,(int)G[to].size()});

  G[to].push_back((edge){from,0,(int)G[from].size()-1});

}

void bfs(int s){

  memset(level,-1,sizeof(level));

  queue<int>q;

  level[s]=0;

  q.push(s);

  while(!q.empty()){

    int v=q.front();q.pop();

    for(int i=0;i<(int)(G[v].size());i++){

      edge &e=G[v][i];

      if(e.cap>0&&level[e.to]<0){

        level[e.to]=level[v]+1;

        q.push(e.to);

      }

    }

  }

}

int dfs(int v,int t,int f){

  if(v==t)return f;

  for(int &i=iter[v];i<(int)(G[v].size());i++){

    edge &e=G[v][i];

    if(e.cap>0&&level[v]<level[e.to]){

      int 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;

}

int max_flow(int s,int t){

  int flow=0;

  while(1){

    bfs(s);

    if(level[t]<0)return flow;

    memset(iter,0,sizeof(iter));

    int f;

    while((f=dfs(s,t,INF))>0){

      flow+=f;

    }

  }

}

int main(){

  int x,y,n,s,t;

  vector<pair<int,int>> A,B;

  cin>>n;

  x=n;

  y=n;

  for(int i=0;i<n;i++){

  	cin>>s>>t;

  	A.push_back({s,t});

  }

  for(int i=0;i<n;i++){

  	cin>>s>>t;

  	B.push_back({s,t});

  }

  for(int i=0;i<n;i++){

  	for(int j=0;j<n;j++){

  		if(A[i].first<B[j].first&&A[i].second<B[j].second){

  			add_edge(i+1,n+j+1,1);

  		}

  	}

  }

  for(int i=0;i<y;i++){

  	add_edge(x+i+1,x+y+1,1);

  }

  for(int i=0;i<x;i++){

  	add_edge(0,i+1,1);

  }

  cout<<max_flow(0,x+y+1)<<endl;

}


// Faster C++ solution for the same problem (measured speedup: 4.228851839958568x):
#include <bits/stdc++.h>

using namespace std;

using ll = long long;

using vi = vector<int>;



#define SZ(a) ((int)a.size())

#define ALL(a) a.begin(), a.end()

#define MP make_pair

#define PB push_back

#define EB emplace_back

#define fst first

#define snd second



void dout() { cerr << endl; }



template <typename Head, typename... Tail>

void dout(Head H, Tail... T) {

    cerr << H << ' ';

    dout(T...);

}





vector<vi> g;

vector<char> used;

vi mt;



bool dfs(int u)

{

    if (used[u])

        return false;

    used[u] = true;

    for (auto v : g[u])

    {

        if (mt[v] == -1 || dfs(mt[v]))

        {

            mt[v] = u;

            return true;

        }

    }

    return false;

}



int main()

{

    ios::sync_with_stdio(false);

    cin.tie(nullptr);

    int n;

    cin >> n;

    g.resize(n);

    vector<pair<int, int>> a(n), b(n);

    mt.assign(n, -1);

    for (int i = 0; i < n; i++)

    {

        cin >> a[i].fst >> a[i].snd;

    }

    for (int i = 0; i < n; i++)

    {

        cin >> b[i].fst >> b[i].snd;

    }

    for (int i = 0; i < n; i++)

    {

        int x1, y1;

        tie(x1, y1) = a[i];

        for (int j = 0; j < n; j++)

        {

            int x2, y2;

            tie(x2, y2) = b[j];

            if (x1 < x2 && y1 < y2)

            {

                g[i].PB(j);

            }

        }

    }

    int ans = 0;

    for (int i = 0; i < n; i++)

    {

        used.assign(n, false);

        if (dfs(i))

            ans++;

    }

    cout << ans;

    return 0;

}