#![allow(unused_imports)]
use std::io::{ self, Write };
use std::str::FromStr;
use std::cmp::{ min, max };
use std::collections::{ BinaryHeap, VecDeque };

#[allow(unused_macros)]
macro_rules! trace {
    ($var:expr) => {
        let _ = writeln!(&mut std::io::stderr(), ">>> {} = {:?}", stringify!($var), $var);
    };
    ($($args:expr),*) => { trace!(($($args),*)) }
}

#[allow(unused_macros)]
macro_rules! put {
    ($var:expr) => {
        let _ = writeln!(&mut std::io::stdout(), "{}", $var);
    };
    ($var:expr, $($args:expr),*) => {
        let _ = write!(&mut std::io::stdout(), "{} ", $var);
        put!($($args),*);
    };
}

mod Dinic {
    use std::collections::VecDeque;
    use std::cmp::min;

    pub type Weight = i32;
    pub type WeightedGraph = Vec<Vec<(usize, Weight)>>; // [u][] = (v, w)
    pub type Edge = (usize, usize, Weight);

    pub fn new(n: usize, es: &Vec<Edge>) -> WeightedGraph {
        let mut g: WeightedGraph = vec![vec![]; n];
        for &(u, v, w) in es.iter() {
            g[u].push((v, w));
            g[v].push((u, 0));
        }
        g
    }

    fn levelize(g: &WeightedGraph, s: usize, t: usize, cap: &Vec<Vec<Weight>>, flw: &Vec<Vec<Weight>>) -> Vec<i32> {
        let n = g.len();
        let mut level = vec![-1; n]; level[s] = 0;
        let mut q = VecDeque::new(); q.push_back(s);
        while let Some(u) = q.pop_front() {
            if u == t { break }
            for &(v, _) in g[u].iter() {
                if level[v] == -1 && cap[u][v] != flw[u][v] {
                    level[v] = level[u] + 1;
                    q.push_back(v);
                }
            }
        }
        level
    }

    fn augment(limit: Weight,
               level: &Vec<i32>, mut used: &mut Vec<bool>,
               g: &WeightedGraph, u: usize, t: usize,
               cap: &Vec<Vec<Weight>>, mut flw: &mut Vec<Vec<Weight>>) -> Weight {
        if u == t {
            limit
        } else if used[u] || limit == 0 {
            0
        } else {
            used[u] = true;
            for &(v, _) in g[u].iter() {
                if level[v] > level[u] {
                    let limit2 = min(limit, cap[u][v] - flw[u][v]);
                    let f = augment(limit2, &level, &mut used, &g, v, t, &cap, &mut flw);
                    if f > 0 {
                        flw[u][v] += f; flw[v][u] -= f;
                        used[u] = false;
                        return f;
                    }
                }
            }
            0
        }
    }

    pub fn flow(g: &WeightedGraph, s: usize, t: usize) -> Weight {

        let n = g.len();

        let mut cap = vec![vec![0; n]; n];
        let mut flw = vec![vec![0; n]; n];
        for u in 0..n { for &(v, w) in g[u].iter() { cap[u][v] = w; } }

        let inf = Weight::max_value() / 8;
        let mut sum = 0;
        loop {
            let level = levelize(&g, s, t, &cap, &flw);
            if level[t] == -1 { break }
            let mut used = vec![false; n];
            sum += augment(inf, &level, &mut used, &g, s, t, &cap, &mut flw);
        }

        sum
    }
}

fn main() {
    let mut sc = Scanner::new();
    let n = sc.cin();

    let mut ws = vec![];
    for i in 0..n {
        let a :usize = sc.cin();
        let b :usize = sc.cin();
        ws.push((a, b));
    }

    let mut bs = vec![];
    for i in 0..n {
        let a :usize = sc.cin();
        let b :usize = sc.cin();
        bs.push((a, b));
    }

    let mut es = vec![];

    for i in 0..n {
        for j in 0..n {
            if ws[i].0 < bs[j].0 && ws[i].1 < bs[j].1 {
                es.push((1+i, 1+n+j, 1));
            }
        }
    }
    for i in 0..n {
        es.push((0, 1+i, 1));
        es.push((1+n+i, 2*n+1, 1));
    }

    let g = Dinic::new(2*n+2, &es);
    println!("{}", Dinic::flow(&g, 0, 2*n+1));

}

#[allow(dead_code)]
struct Scanner { stdin: io::Stdin, buffer: VecDeque<String>, }
#[allow(dead_code)]
impl Scanner {
    fn new() -> Scanner { Scanner { stdin: io::stdin(), buffer: VecDeque::new() } }
    fn reserve(&mut self) {
        while self.buffer.len() == 0 {
            let mut line = String::new();
            let _ = self.stdin.read_line(&mut line);
            for w in line.split_whitespace() {
                self.buffer.push_back(String::from(w));
            }
        }
    }
    fn cin<T: FromStr>(&mut self) -> T {
        self.reserve();
        match self.buffer.pop_front().unwrap().parse::<T>() {
            Ok(a) => a,
            Err(_) => panic!("parse err")
        }
    }
}


Measured memory_bytes: 4352