#include<bits/stdc++.h>
#define range(i,a,b) for(int i = (a); i < (b); i++)
#define rep(i,b) for(int i = 0; i < (b); i++)
#define all(a) (a).begin(), (a).end()
#define show(x)  cerr << #x << " = " << (x) << endl;
#define int long long
using namespace std;

template <typename X, typename T>
auto vectors(X x, T a) {
	return vector<T>(x, a);
}

template <typename X, typename Y, typename Z, typename... Zs>
auto vectors(X x, Y y, Z z, Zs... zs) {
	auto cont = vectors(y, z, zs...);
	return vector<decltype(cont)>(x, cont);
}

template<typename T>
ostream& operator << (ostream& os, vector<T>& v){
	rep(i,v.size()){ os << v[i] << (i == v.size() - 1 ? "" : " "); } return os;
}
template<typename T>
istream& operator >> (istream& is, vector<T>& v){
	for(T& x: v){ is >> x; } return is;
}

struct edge {
	int to;
	int label;
};


class EdmondsBlossomDecomposition{
};

vector<pair<int, int>> gabow_edmonds(const vector<vector<int>>& gra){
	int N = gra.size();
	vector<vector<edge>> g(N + 1);
	vector<pair<int, int>> edges;
	{
		int cnt = N + 1;
		for(int i = 0;i < N;i++){
			for(auto to : gra[i]){
				if(i < to){
					g[to + 1].push_back({i + 1, cnt});
					g[i + 1].push_back({to + 1, cnt++});
					edges.push_back({i + 1, to + 1});
				}
			}
		}
	}
	vector<int> mate(N + 1 , 0);
	vector<int> label(N + 1 , -1);
	vector<int> first(N + 1 , 0);
	queue<int> que;

	// first$B$NCY1dI>2A(B
	function<int(int)> eval_first = [&](int x){
		if(label[first[x]] < 0) return first[x];
		first[x] = eval_first(first[x]);
		return first[x];
	};

	// $B%5%V%k!<%A%s(BR
	function<void(int, int)> rematch = [&](int v, int w){
		// R1
		int t = mate[v];
		mate[v] = w;
		if(mate[t] != v) return;
		// R2
		if(label[v] <= N){
			mate[t] = label[v];
			rematch(label[v] , t);
		}
		// R3
		else{
			int x = edges[label[v] - N - 1].first;
			int y = edges[label[v] - N - 1].second;
			rematch(x , y);
			rematch(y , x);
		}
	};

	function<void(int, int, int)> assignLabel = [&](int x, int y, int num){
		// L0
		int r = eval_first(x);
		int s = eval_first(y);
		int join = 0;
		if(r == s) return;
		// -num$B$,%U%i%0(B
		label[r] = -num;
		label[s] = -num;
		while(true){
			// L1
			if(s != 0) swap(r , s);
			// L2
			r = eval_first(label[mate[r]]);
			if(label[r] == -num){
				join = r;
				break;
			}
			label[r] = -num;
		}
		// L3
		int v = first[x];
		// L4
		while(v != join){
			que.push(v);
			label[v] = num;
			first[v] = join;
			v = first[label[mate[v]]];
		}
		// L3
		v = first[y];
		// L4
		while(v != join){
			que.push(v);
			label[v] = num;
			first[v] = join;
			v = first[label[mate[v]]];
		}
		// L5$B$OCY1dI>2A$7$F$$$k$?$aITMW(B
		// L6
		return;
	};

	function<bool(int)> augment_check = [&](int u){
		// E1 $B8eH>(B
		first[u] = 0;
		label[u] = 0;
		que.push(u);
		while(!que.empty()){
			// E2
			int x = que.front();
			que.pop();
			for(auto e : g[x]){
				int y = e.to;
				// E3
				if(mate[y] == 0 && y != u){
					mate[y] = x;
					rematch(x , y);
					return true;
				}
				// E4
				else if(label[y] >= 0){
					assignLabel(x , y , e.label);
				}
				// E5
				else if(label[mate[y]] < 0){
					label[mate[y]] = x;
					first[mate[y]] = y;
					que.push(mate[y]);
				}
				// E6
			}
		}
		return false;
	};

	for(int i = 1;i <= N;i++){
		// E1
		que = queue<int>();
		if(mate[i] != 0) continue;
		if(augment_check(i)){
			// E7
			fill(label.begin(), label.end(), -1);
		}
	}

	vector<pair<int, int>> ans;
	for(int i = 1;i <= N;i++){
		if(i < mate[i]){
			ans.push_back({i , mate[i]});
		}
	}
	return ans;
}

signed main(){
	int n;
	cin >> n;

	vector<pair<int, int>> a(n), b(n);
	rep(i,n) cin >> a[i].first >> a[i].second;
	rep(i,n) cin >> b[i].first >> b[i].second;

	vector<vector<int>> g(n * 2);
	rep(i,n){
		rep(j,n){
			if(a[i].first < b[j].first and a[i].second < b[j].second){
				g[i].emplace_back(n + j);
				g[n + j].emplace_back(i);
			}
		}
	}
	cout << gabow_edmonds(g).size() << endl;
}


Measured memory_bytes: 512