using System;
using System.Collections;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Threading.Tasks;

class Program
{
	/*
	・概要
	最大流問題の解決に使用
	計算量は、O(V^2 * E)
	（頂点の数がV、辺の数がE）

	・（最大）二部マッチング問題
	二集団の前後にノード（スタートとゴール）を付け加えることで、最大流問題に帰着可能
	（cf. 重みづけがない場合は、フローを全て1にすれば良いだけ）

	・使用法
	有向グラフの辺の数だけAddEdgeを呼び出し、辺の始点、終点、フローを与える。
	MaxFlowが最大フローを返す。

	*/

	//Main
	static void Main(string[] args)
	{
		//読み込む
		int N = int.Parse(Console.ReadLine());

		//x座標とy座標を合わせたジャグ配列
		int[][] redCoord = new int[N][];
		//ソート の都合上、{x,y}とする
		int[][] blueCoord = new int[N][];

		//読み込む
		for(int i=0; i<N; i++){
			string[] S = Console.ReadLine().Split(' ');
			int a = int.Parse(S[0]);
			int b = int.Parse(S[1]);

			redCoord[i] = new int[2]{a,b};
		}
		for(int i=0; i<N; i++){
			string[] S = Console.ReadLine().Split(' ');
			int c = int.Parse(S[0]);
			int d = int.Parse(S[1]);

			blueCoord[i] = new int[2]{c,d};
		}

		//1. FordFulkerson.Graphクラスのインスタンスを作成
		//ノード数は、両集団の構成員数＋２（スタートとゴール）
		FordFulkerson.Graph maxPair = new FordFulkerson.Graph(N+N+2);

		//2. 辺を結合
		/*
		まず、スタートと赤と、青とゴールとを全て繋ぐ
		スタートはノード2N、ゴールはノード2N+1（= 2N+2 - 1）とする
		*/
		int start = 2*N;
		int goal = 2*N + 1;
		for(int i=0; i<N; i++){
			int redNode = i;
			int blueNode = i+N;
			maxPair.AddEdge(start,redNode,1);
			maxPair.AddEdge(blueNode,goal,1);
		}

		//次に、赤と青との間に辺を結合できる場合（マッチング可能な場合）、辺を追加
		/*
		cf. この段階で、O(N*N)時間かかる
		*/
		for(int i=0; i<N; i++){
			for(int j=0; j<N; j++){
				int redNode = i;
				int blueNode = j+N;
				//結合可能な場合
				if(redCoord[i][0] < blueCoord[j][0] && redCoord[i][1] < blueCoord[j][1]){
					//重みはないので、常にフローは1
					maxPair.AddEdge(redNode,blueNode,1);
				}
			}
		}

		//スタートからゴールまでの最大流を求める
		int ans = maxPair.MaxFlow(start,goal);

		Console.WriteLine(ans);
	}
}

//FordFulkerson法用の名前空間
/*
2つのクラスを内包
・辺を意味するEdge
・グラフとして問題を解くGraph
*/

/*
全てclass Graphからアクセスのこと
（e.g.
・初期化
	FordFulkerson.Graph maxPair = new FordFulkerson.Graph(N+N+2);

・エッジの追加
	maxPair.AddEdge(start,goal,1);
）
*/

namespace FordFulkerson
{
	class Edge
	{
	  public Edge(int begin, int end, int weight)
	  {
	    this.Begin = begin;
	    this.End = end;
	    this.Weight = weight;
	  }

	  public int Begin { get; private set; }
	  public int End { get; private set; }
	  public int Weight { get; private set; }

	  public override string ToString()
	  {
	    return string.Format(
	      "Begin[{0}], End[{1}], Weight[{2}]",
	      Begin, End, Weight);
	  }
	}

	class Graph
	{
	  private Dictionary<int, List<Edge>> _adjacentEdges
	    = new Dictionary<int, List<Edge>>();

		//コンストラクタ
		/*
		全体のノード数で初期化
		*/
	  public Graph(int vertexCount)
	  {
	    this.VertexCount = vertexCount;
	  }

	  public int VertexCount { get; private set; }

	  public IEnumerable<int> Vertices
	  {
	    get
	    {
	      return _adjacentEdges.Keys;
	    }
	  }

	  public IEnumerable<Edge> Edges
	  {
	    get
	    {
	      return _adjacentEdges.Values.SelectMany(e => e);
	    }
	  }

	  public int EdgeCount
	  {
	    get
	    {
	      return this.Edges.Count();
	    }
	  }

		//辺を追加
		/*
		引数：出発点、行き先、重み（その辺の最大流）
		（＝始点、終点、フロー）
		*/
	  public void AddEdge(int begin, int end, int weight)
	  {
	    if (!_adjacentEdges.ContainsKey(begin))
	    {
	      var edges = new List<Edge>();
	      _adjacentEdges.Add(begin, edges);
	    }

	    _adjacentEdges[begin].Add(new Edge(begin, end, weight));
	  }

		//最大流を返す
		/*
		引数：出発点、行き先（スタートとゴール）
		*/
	  public int MaxFlow(int s, int t)
	  {
	    int u, v;

	    // Create a residual graph and fill the residual graph with
	    // given capacities in the original graph as residual capacities
	    // in residual graph
	    int[,] residual = new int[VertexCount, VertexCount];

	    // Residual graph where rGraph[i,j] indicates
	    // residual capacity of edge from i to j (if there
	    // is an edge. If rGraph[i,j] is 0, then there is not)
	    for (u = 0; u < VertexCount; u++)
	      for (v = 0; v < VertexCount; v++)
	        residual[u, v] = 0;
	    foreach (var edge in this.Edges)
	    {
	      residual[edge.Begin, edge.End] = edge.Weight;
	    }

	    // This array is filled by BFS and to store path
	    int[] parent = new int[VertexCount];

	    // There is no flow initially
	    int maxFlow = 0;

	    // Augment the flow while there is path from source to sink
	    while (BFS(residual, s, t, parent))
	    {
	      // Find minimum residual capacity of the edhes along the
	      // path filled by BFS. Or we can say find the maximum flow
	      // through the path found.
	      int pathFlow = int.MaxValue;
	      for (v = t; v != s; v = parent[v])
	      {
	        u = parent[v];
	        pathFlow = pathFlow < residual[u, v]
	          ? pathFlow : residual[u, v];
	      }

	      // update residual capacities of the edges and reverse edges
	      // along the path
	      for (v = t; v != s; v = parent[v])
	      {
	        u = parent[v];
	        residual[u, v] -= pathFlow;
	        residual[v, u] += pathFlow;
	      }

	      // Add path flow to overall flow
	      maxFlow += pathFlow;
	    }

	    // Return the overall flow
	    return maxFlow;
	  }

	  // Returns true if there is a path from source 's' to sink 't' in
	  // residual graph. Also fills parent[] to store the path.
	  private bool BFS(int[,] residual, int s, int t, int[] parent)
	  {
	    bool[] visited = new bool[VertexCount];
	    for (int i = 0; i < visited.Length; i++)
	    {
	      visited[i] = false;
	    }

	    Queue<int> q = new Queue<int>();

	    visited[s] = true;
	    q.Enqueue(s);
	    parent[s] = -1;

	    // standard BFS loop
	    while (q.Count > 0)
	    {
	      int u = q.Dequeue();

	      for (int v = 0; v < VertexCount; v++)
	      {
	        if (!visited[v]
	          && residual[u, v] > 0)
	        {
	          q.Enqueue(v);
	          visited[v] = true;
	          parent[v] = u;
	        }
	      }
	    }

	    // If we reached sink in BFS starting from source,
	    // then return true, else false
	    return visited[t] == true;
	  }
	}
}



Measured memory_bytes: 19476