#include <cmath>
#include <queue>
#include <type_traits>
#include <cstddef>
#include <utility>
#include <limits>
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
namespace loquat {
using vertex_t = size_t;
}
namespace loquat {
namespace edge_param {
struct to_ {
	vertex_t to;
	explicit to_(vertex_t t = 0)
		: to(t)
	{ }
};
template <typename T>
struct capacity_ {
	using capacity_type = T;
	capacity_type capacity;
	explicit capacity_(const capacity_type& c = capacity_type())
		: capacity(c)
	{ }
};
template <typename T>
using capacity = capacity_<T>;
}
namespace detail {
template <typename T, typename... Params>
struct edge_param_wrapper : public T, edge_param_wrapper<Params...> {
	template <typename U, typename... Args>
	explicit edge_param_wrapper(U&& x, Args&&... args)
		: T(std::forward<U>(x))
		, edge_param_wrapper<Params...>(std::forward<Args>(args)...)
	{ }
};
template <typename T>
struct edge_param_wrapper<T> : public T {
	template <typename U>
	explicit edge_param_wrapper(U&& x)
		: T(std::forward<U>(x))
	{ }
};
}
template <typename... Params>
struct edge : public detail::edge_param_wrapper<edge_param::to_, Params...> {
	template <typename... Args>
	explicit edge(Args&&... args)
		: detail::edge_param_wrapper<edge_param::to_, Params...>(
			std::forward<Args>(args)...)
	{ }
};
}
namespace loquat {
template <typename EdgeType>
class adjacency_list {
public:
	using edge_type = EdgeType;
	using edge_list = std::vector<edge_type>;
private:
	std::vector<std::vector<EdgeType>> m_edges;
public:
	explicit adjacency_list(size_t n)
		: m_edges(n)
	{ }
	size_t size() const {
		return m_edges.size();
	}
	const edge_list& operator[](vertex_t u) const {
		return m_edges[u];
	}
	edge_list& operator[](vertex_t u){
		return m_edges[u];
	}
	template <typename... Args>
	void add_edge(vertex_t from, Args&&... args){
		m_edges[from].emplace_back(std::forward<Args>(args)...);
	}
};
}
namespace loquat {
template <typename EdgeType>
struct has_weight {
private:
	template <typename U>
	static std::false_type check_type(...);
	static std::false_type check_member(...);
public:
	static const bool value =
		decltype(check_type<EdgeType>(nullptr))::value &&
		decltype(check_member(std::declval<EdgeType>()))::value;
};
}
namespace loquat {
namespace detail {
template <typename EdgeType>
auto negate_weight(EdgeType&)
	-> typename std::enable_if<!has_weight<EdgeType>::value, void>::type
{ }
}
template <typename EdgeType>
struct residual_edge : public EdgeType {
	using base_type = EdgeType;
	size_t rev;
	residual_edge(const base_type& e, size_t rev)
		: base_type(e)
		, rev(rev)
	{ }
};
template <typename EdgeType>
adjacency_list<residual_edge<EdgeType>>
make_residual(const adjacency_list<EdgeType>& graph){
	using edge_type = EdgeType;
	using residual_type = residual_edge<edge_type>;
	using capacity_type = typename edge_type::capacity_type;
	const size_t n = graph.size();
	adjacency_list<residual_type> result(n);
	for(vertex_t u = 0; u < n; ++u){
		for(const auto& e : graph[u]){
			result.add_edge(u, residual_type(e, 0));
		}
	}
	for(vertex_t u = 0; u < n; ++u){
		const size_t m = graph[u].size();
		for(size_t i = 0; i < m; ++i){
			auto e = graph[u][i];
			const auto v = e.to;
			e.to = u;
			e.capacity = capacity_type();
			detail::negate_weight(e);
			result[u][i].rev = result[v].size();
			result.add_edge(v, residual_type(e, i));
		}
	}
	return result;
}
}
namespace loquat {
template <typename T>
constexpr inline auto positive_infinity() noexcept
	-> typename std::enable_if<std::is_integral<T>::value, T>::type
{
	return std::numeric_limits<T>::max();
}
}
namespace loquat {
namespace detail {
template <typename EdgeType>
class maxflow_dinitz_impl {
public:
	using edge_type = EdgeType;
	using capacity_type = typename edge_type::capacity_type;
private:
	adjacency_list<edge_type>& m_graph;
	std::vector<size_t> m_levels;
	std::vector<size_t> m_iterations;
	vertex_t m_source;
	vertex_t m_sink;
	explicit maxflow_dinitz_impl(
		vertex_t source,
		vertex_t sink,
		adjacency_list<edge_type>& graph)
		: m_graph(graph)
		, m_levels()
		, m_iterations()
		, m_source(source)
		, m_sink(sink)
	{ }
	std::vector<size_t> compute_level_map(vertex_t source) const {
		const auto n = m_graph.size();
		std::vector<size_t> levels(n, 0);
		std::queue<vertex_t> q;
		levels[source] = 1;
		q.push(source);
		while(!q.empty()){
			const auto u = q.front();
			q.pop();
			for(const auto& e : m_graph[u]){
				const auto v = e.to;
				if(e.capacity <= 0 || levels[v] != 0){ continue; }
				levels[v] = levels[u] + 1;
				q.push(v);
			}
		}
		return levels;
	}
	capacity_type dfs_recur(vertex_t u, capacity_type limit){
		if(u == m_sink){ return limit; }
		for(; m_iterations[u] < m_graph[u].size(); ++m_iterations[u]){
			auto& e = m_graph[u][m_iterations[u]];
			const auto v = e.to;
			if(e.capacity <= 0 || m_levels[u] >= m_levels[v]){ continue; }
			const auto diff = dfs_recur(v, std::min(e.capacity, limit));
			if(diff > 0){
				e.capacity -= diff;
				m_graph[v][e.rev].capacity += diff;
				return diff;
			}
		}
		return 0;
	}
	capacity_type solve(){
		const auto inf = positive_infinity<capacity_type>();
		const auto n = m_graph.size();
		capacity_type flow = 0;
		while(true){
			m_levels = compute_level_map(m_source);
			if(m_levels[m_sink] == 0){ break; }
			m_iterations.assign(n, 0);
			while(true){
				const auto f = dfs_recur(m_source, inf);
				if(f <= 0){ break; }
				flow += f;
			}
		}
		return flow;
	}
public:
	static capacity_type solve(
		vertex_t source, vertex_t sink, adjacency_list<EdgeType>& graph)
	{
		maxflow_dinitz_impl<edge_type> self(source, sink, graph);
		return self.solve();
	}
};
}
template <typename EdgeType>
typename EdgeType::capacity_type
maxflow_dinitz(
	vertex_t source,
	vertex_t sink,
	adjacency_list<EdgeType>& graph)
{
	using edge_type = EdgeType;
	return detail::maxflow_dinitz_impl<edge_type>::solve(source, sink, graph);
}
}
using namespace std;
using pii = pair<int, int>;
using edge = loquat::edge<loquat::edge_param::capacity<int>>;
int main(){
	ios_base::sync_with_stdio(false);
	int n;
	cin >> n;
	vector<pii> r(n), b(n);
	for(int i = 0; i < n; ++i){
		cin >> r[i].first >> r[i].second;
	}
	for(int i = 0; i < n; ++i){
		cin >> b[i].first >> b[i].second;
	}
	loquat::adjacency_list<edge> g(2 * n + 2);
	const int source = 2 * n, sink = source + 1;
	for(int i = 0; i < n; ++i){
		g.add_edge(source, i, 1);
		g.add_edge(i + n, sink, 1);
	}
	for(int i = 0; i < n; ++i){
		for(int j = 0; j < n; ++j){
			if(r[i].first < b[j].first && r[i].second < b[j].second){
				g.add_edge(i, j + n, 1);
			}
		}
	}
	auto residual = loquat::make_residual(g);
	const auto answer = loquat::maxflow_dinitz(source, sink, residual);
	cout << answer << endl;
	return 0;
}


Measured memory_bytes: 512