#[allow(unused_macros)]
macro_rules ! input { ( source = $ s : expr , $ ( $ r : tt ) * ) => { let mut iter = $ s . split_whitespace ( ) ; let mut next = || { iter . next ( ) . unwrap ( ) } ; input_inner ! { next , $ ( $ r ) * } } ; ( $ ( $ r : tt ) * ) => { let stdin = std :: io :: stdin ( ) ; let mut bytes = std :: io :: Read :: bytes ( std :: io :: BufReader :: new ( stdin . lock ( ) ) ) ; let mut next = move || -> String { bytes . by_ref ( ) . map ( | r | r . unwrap ( ) as char ) . skip_while ( | c | c . is_whitespace ( ) ) . take_while ( | c |! c . is_whitespace ( ) ) . collect ( ) } ; input_inner ! { next , $ ( $ r ) * } } ; }
#[allow(unused_macros)]
macro_rules ! input_inner { ( $ next : expr ) => { } ; ( $ next : expr , ) => { } ; ( $ next : expr , $ var : ident : $ t : tt $ ( $ r : tt ) * ) => { let $ var = read_value ! ( $ next , $ t ) ; input_inner ! { $ next $ ( $ r ) * } } ; }
#[allow(unused_macros)]
macro_rules ! read_value { ( $ next : expr , ( $ ( $ t : tt ) ,* ) ) => { ( $ ( read_value ! ( $ next , $ t ) ) ,* ) } ; ( $ next : expr , [ $ t : tt ; $ len : expr ] ) => { ( 0 ..$ len ) . map ( | _ | read_value ! ( $ next , $ t ) ) . collect ::< Vec < _ >> ( ) } ; ( $ next : expr , chars ) => { read_value ! ( $ next , String ) . chars ( ) . collect ::< Vec < char >> ( ) } ; ( $ next : expr , usize1 ) => { read_value ! ( $ next , usize ) - 1 } ; ( $ next : expr , $ t : ty ) => { $ next ( ) . parse ::<$ t > ( ) . expect ( "Parse error" ) } ; }

fn main() {
    input! {
        n: usize,
        ab: [(u64, u64); n],
        cd: [(u64, u64); n]
    }

    let s = 2*n;
    let t = 2*n+1;
    let mut flow = Flow::new(2*n+2);
    for i in 0..n {
        flow.add_edge(s, i, 1);
        flow.add_edge(i+n, t, 1);
    }
    for i in 0..n {
        let (a, b) = ab[i];
        for j in 0..n {
            let (c, d) = cd[j];
            if a < c && b < d {
                flow.add_edge(i, j+n, 1);
            }
        }
    }
    println!("{}", flow.max_flow(s, t));
}

#[allow(dead_code)]
#[derive(Debug)]
struct Flow {
    #[doc = " to, rev, capacity"]
    edges: Vec<Vec<(usize, usize, u64)>>,
}
#[allow(dead_code)]
impl Flow {
    pub fn new(n: usize) -> Flow {
        Flow {
            edges: vec![vec![]; n],
        }
    }
    pub fn add_edge(&mut self, from: usize, to: usize, cap: u64) {
        let from_rev = self.edges[to].len();
        let to_rev = self.edges[from].len();
        self.edges[from].push((to, from_rev, cap));
        self.edges[to].push((from, to_rev, 0));
    }
    pub fn max_flow(&mut self, s: usize, t: usize) -> u64 {
        let mut flow = 0;
        loop {
            let level = self.bfs(s);
            let mut iter = vec![0; self.edges.len()];
            if level[t].is_none() {
                return flow;
            }
            loop {
                let f = self.dfs(s, t, u64::max_value(), &level, &mut iter);
                if f == 0 {
                    break;
                }
                flow += f;
            }
        }
    }
    fn bfs(&self, s: usize) -> Vec<Option<usize>> {
        let mut level = vec![None; self.edges.len()];
        let mut que = std::collections::VecDeque::new();
        level[s] = Some(0);
        que.push_back(s);
        while let Some(v) = que.pop_front() {
            let l = Some(level[v].unwrap() + 1);
            for &(to, _, cap) in &self.edges[v] {
                if cap > 0 && level[to].is_none() {
                    level[to] = l;
                    que.push_back(to);
                }
            }
        }
        level
    }
    fn dfs(
        &mut self,
        v: usize,
        t: usize,
        f: u64,
        level: &[Option<usize>],
        iter: &mut [usize],
    ) -> u64 {
        if v == t {
            return f;
        }
        for i in iter[v]..self.edges[v].len() {
            iter[v] = i;
            let (to, rev, cap) = self.edges[v][i];
            if cap > 0 && level[v].unwrap() < level[to].unwrap_or(0) {
                let d = self.dfs(to, t, std::cmp::min(f, cap), level, iter);
                if d > 0 {
                    self.edges[v][i].2 -= d;
                    self.edges[to][rev].2 += d;
                    return d;
                }
            }
        }
        0
    }
}

Measured memory_bytes: 4352