//
//  main.cpp
//
// ABC133-F

#include <algorithm>
#include <array>
#include <assert.h>
#include <functional>
#include <iostream>
#include <limits>
#include <map>
#include <math.h>
#include <memory>
#include <queue>
#include <random>
#include <set>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <vector>

using namespace std;

using ll = long long;
using ull = unsigned long long;

namespace Util {
    int bipartite_match(size_t, size_t, const function<bool(size_t, size_t)>&);
    int min_path_decomp_dag(size_t, const function<bool(size_t, size_t)>&);
    int clamp_sum(int, int);
};

template <typename INT>
struct Network {
    struct Edge {
        size_t to; // 行き先
        INT cap; // 容量
        INT cost; // コスト
        size_t rev; // 逆辺インデックス
        Edge(size_t to, INT cap, INT cost, size_t rev): to(to), cap(cap), cost(cost), rev(rev) {};
    };
    vector<vector<Edge>> graph; // グラフの隣接リスト表現
    // コンストラクタ
    explicit Network(size_t vertices) {
        graph.resize(vertices);
    }
    // 辺を追加する（コスト0）
    void add_edge(size_t from, size_t to, INT cap) {
        assert(from < graph.size() && to < graph.size());
        graph[from].emplace_back(to, cap, 0, graph[to].size());
        graph[to].emplace_back(from, 0, 0, graph[from].size() - 1);
    }
    // 辺を追加する（コスト指定）
    void add_edge(size_t from, size_t to, INT cap, INT cost) {
        assert(from < graph.size() && to < graph.size());
        graph[from].emplace_back(to, cap, cost, graph[to].size());
        graph[to].emplace_back(from, 0, -cost, graph[from].size() - 1);
    }
    // Ford-Fulkerson法で最大流を求める（コスト無視）
    INT max_flow_ff(size_t s, size_t t) {
        assert(s < graph.size() && t < graph.size());
        INT max_flow = 0;
        while (true) {
            vector<bool> used(graph.size(), false);
            INT flow = dfs_path(s, t, numeric_limits<INT>::max(), used);
            if (flow == 0) {
                return max_flow;
            }
            max_flow += flow;
        }
    }
    // 始点vから終点tへのパスをDFSで見つけて水を流す（コスト無視）。
    // 流した分だけ各辺のcapacityが変化する。
    // return 流した流量
    // max_flow 見つけたパスに流す流量の最大値。それ以上は（流せても）流さない。
    // used 訪問済み頂点フラグ
    INT dfs_path(size_t v, size_t t, INT max_flow, vector<bool>& used) {
        assert(used.size() == graph.size());
        assert(v < graph.size() && t < graph.size());
        if (v == t) {
            return max_flow;
        }
        used[v] = true;
        for (Edge& e : graph[v]) {
            if (used[e.to] || e.cap == 0) { // 行き先が訪問済みか、これ以上流せない辺なら
                continue;
            }
            INT flow = dfs_path(e.to, t, min(e.cap, max_flow), used);
            if (flow == 0) {
                continue;
            }
            e.cap -= flow;
            graph[e.to][e.rev].cap += flow;
            return flow;
        }
        return 0;
    }
};

namespace Util {
    // 二部グラフの最大マッチングを求める
    int bipartite_match(size_t u_size, size_t v_size, const function<bool(size_t, size_t)>& is_connected) {
        Network<int> net(u_size + v_size + 2); // 0=start, 1=end, uたち, vたち
        const size_t U_START = 2;
        const size_t V_START = U_START + u_size;
        for (size_t u = 0; u < u_size; u++) {
            net.add_edge(0, u + U_START, 1);
        }
        for (size_t v = 0; v < v_size; v++) {
            net.add_edge(v + V_START, 1, 1);
        }
        for (size_t u = 0; u < u_size; u++) {
            for (size_t v = 0; v < v_size; v++) {
                if (is_connected(u, v)) {
                    net.add_edge(u + U_START, v + V_START, 1);
                }
            }
        }
        return net.max_flow_ff(0, 1);
    }
}


int main(int argc, char* argv[]) {
	int N;
	cin >> N;
	set<pair<int, int>> edges;
	vector<pair<int, int>> reds;
	for (int i = 0; i < N; i++) {
		int a, b;
		cin >> a >> b;
		reds.push_back({a, b});
	}
	for (int j = 0; j < N; j++) {
		int c, d;
		cin >> c >> d;
		for (int i = 0; i < N; i++) {
			if (reds[i].first < c && reds[i].second < d) {
				edges.insert({i, j});
			}
		}
	}
	cout << Util::bipartite_match(N, N, [&edges](size_t i, size_t j){
		return edges.find({i, j}) != edges.end();
	}) << endl;
}
    


Measured memory_bytes: 640