#include <algorithm>
#include <bits/stdc++.h>
#include <cmath>
#include <cstddef>
#include <functional>
#include <iostream>
#include <limits>
#include <queue>
#include <string>
#include <type_traits>
#include <vector>

// lib/vector2.hpp
#ifndef VECTOR2_HPP
#define VECTOR2_HPP


/*
usage:

tools::vector2<int>(6, 5) // => (6, 5)
*/

namespace tools {
  template <typename T>
  class vector2 {
  public:
    T x;
    T y;

    vector2() :
      vector2(T(), T()) {
    }

    vector2(const T& x, const T& y) :
      x(x),
      y(y) {
    }

    double norm() const {
      return std::sqrt(this->squaredNorm());
    }

    T squaredNorm() const {
      return this->inner_product(*this);
    }

    template <typename CHECK = T, typename std::enable_if<std::is_same<CHECK, double>::value, std::nullptr_t>::type = nullptr>
    double normalize() const {
      return *this / this->norm();
    }

    vector2<T> operator+() const {
      return *this;
    }

    vector2<T> operator-() const {
      return vector2<T>(-this->x, -this->y);
    }

    friend vector2<T> operator+(const vector2<T>& lhs, const vector2<T>& rhs) {
      return vector2<T>(lhs.x + rhs.x, lhs.y + rhs.y);
    }

    friend vector2<T> operator-(const vector2<T>& lhs, const vector2<T>& rhs) {
      return vector2<T>(lhs.x - rhs.x, lhs.y - rhs.y);
    }

    template <typename T2, typename std::enable_if<!std::is_same<T2, vector2<T>>::value, std::nullptr_t>::type = nullptr>
    friend vector2<T> operator*(const vector2<T>& lhs, const T2& rhs) {
      return vector2<T>(lhs.x * rhs, lhs.y * rhs);
    }
    template <typename T2, typename std::enable_if<!std::is_same<T2, vector2<T>>::value, std::nullptr_t>::type = nullptr>
    friend vector2<T> operator*(const T2& lhs, const vector2<T>& rhs) {
      return vector2<T>(lhs * rhs.x, lhs * rhs.y);
    }

    template <typename T2, typename std::enable_if<!std::is_same<T2, vector2<T>>::value, std::nullptr_t>::type = nullptr>
    friend vector2<T> operator/(const vector2<T>& lhs, const T2& rhs) {
      return vector2<T>(lhs.x / rhs, lhs.y / rhs);
    }

    T inner_product(const vector2<T>& other) const {
      return this->x * other.x + this->y * other.y;
    }

    vector2<T>& operator+=(const vector2<T>& other) {
      return *this = *this + other;
    }

    vector2<T>& operator-=(const vector2<T>& other) {
      return *this = *this - other;
    }

    friend bool operator==(const vector2<T>& lhs, const vector2<T>& rhs) {
      return lhs.x == rhs.x && lhs.y == rhs.y;
    }

    friend bool operator!=(const vector2<T>& lhs, const vector2<T>& rhs) {
      return lhs.x != rhs.x || lhs.y != rhs.y;
    }
  };
}

namespace std {
  template <typename T>
  string to_string(const tools::vector2<T>& f) {
    return '(' + to_string(f.x) + ", " + to_string(f.y) + ')';
  }

  template <typename T>
  struct hash<tools::vector2<T>> {
    typedef std::size_t result_type;
    typedef tools::vector2<T> argument_type;
    std::size_t operator()(const tools::vector2<T>& key) const {
      static const std::hash<T> hash = std::hash<T>();
      std::size_t result = 17;
      result = 31 * result + hash(key.x);
      result = 31 * result + hash(key.y);
      return result;
    }
  };
}

namespace std {
  template <typename T>
  ostream& operator<<(ostream& os, const tools::vector2<T>& self) {
    return os << to_string(self);
  }

  template <typename T>
  istream& operator>>(istream& is, tools::vector2<T>& self) {
    return is >> self.x >> self.y;
  }
}

#endif

// lib/dinic.hpp
#ifndef DINIC_HPP
#define DINIC_HPP


/*
usage:

tools::dinic<std::int_fast64_t> dinic(node_count);
dinic.add_edge(from_node, to_node, capacity);
dinic.calc(s, t) //=> maximum flow
*/

// Source: tkw's diary ( http://vartkw.hatenablog.com/entry/2016/12/02/002703 )
namespace tools {
  template <typename T>
  class dinic {
  private:
    class edge {
    private:
      std::size_t m_id;
      std::size_t m_to;
      T m_capacity;
      std::size_t m_rev_id;

    public:
      edge(const std::size_t id, const std::size_t to, const T capacity, const std::size_t rev_id) :
        m_id(id),
        m_to(to),
        m_capacity(capacity),
        m_rev_id(rev_id) {
      }

      std::size_t id() const {
        return this->m_id;
      }
      std::size_t to() const {
        return this->m_to;
      }
      T capacity() const {
        return this->m_capacity;
      }
      std::size_t rev_id() const {
        return this->m_rev_id;
      }
    };

    class buffer {
    public:
      std::vector<T> capacities;
      std::vector<std::size_t> distances;
      std::vector<std::size_t> next_edges;

      buffer(const dinic& dinic) :
        capacities(dinic.edge_count),
        distances(dinic.node_count(), std::numeric_limits<std::size_t>::max()),
        next_edges(dinic.node_count(), 0) {
        for (const std::vector<edge>& edges_from_node : dinic.edges) {
          for (const edge& edge : edges_from_node) {
            this->capacities[edge.id()] = edge.capacity();
          }
        }
      }

      void reset_for_each_loop() {
        std::fill(this->distances.begin(), this->distances.end(), std::numeric_limits<std::size_t>::max());
        std::fill(this->next_edges.begin(), this->next_edges.end(), 0);
      }
    };

    std::size_t edge_count;
    std::vector<std::vector<edge>> edges;

    void bfs(buffer& buffer, const std::size_t s) const {
      buffer.distances[s] = 0;
      std::queue<std::size_t> queue;
      queue.push(s);
      while (!queue.empty()) {
        const std::size_t from = queue.front();
        queue.pop();
        for (const edge& edge : this->edges[from]) {
          if (buffer.capacities[edge.id()] > 0 && buffer.distances[edge.to()] == std::numeric_limits<std::size_t>::max()) {
            buffer.distances[edge.to()] = buffer.distances[from] + 1;
            queue.push(edge.to());
          }
        }
      }
    }

    T dfs(buffer& buffer, const std::size_t from, const std::size_t t, const T possible_capacity) const {
      if (from == t) return possible_capacity;
      for (std::size_t i = buffer.next_edges[from]; i < this->edges[from].size(); ++i) {
        const edge& edge = this->edges[from][i];
        buffer.next_edges[from] = i;
        if (buffer.capacities[edge.id()] > 0 && buffer.distances[from] < buffer.distances[edge.to()]) {
          const T min_capacity = this->dfs(buffer, edge.to(), t, std::min(possible_capacity, buffer.capacities[edge.id()]));
          if (min_capacity > 0) {
            buffer.capacities[edge.id()] -= min_capacity;
            buffer.capacities[edge.rev_id()] += min_capacity;
            return min_capacity;
          }
        }
      }
      return 0;
    }

  public:
    dinic(const std::size_t node_count) :
      edge_count(0),
      edges(node_count) {
    }

    std::size_t node_count() const {
      return this->edges.size();
    }

    void add_edge(const std::size_t from, const std::size_t to, const T capacity) {
      this->edges[from].emplace_back(this->edge_count, to, capacity, this->edge_count + 1);
      this->edges[to].emplace_back(this->edge_count + 1, from, 0, this->edge_count);
      this->edge_count += 2;
    }

    T calc(const std::size_t s, const std::size_t t) const {
      T result = 0;
      buffer buffer(*this);
      while (true) {
        buffer.reset_for_each_loop();
        this->bfs(buffer, s);
        if (buffer.distances[t] == std::numeric_limits<std::size_t>::max()) return result;
        T min_capacity;
        while ((min_capacity = this->dfs(buffer, s, t, std::numeric_limits<T>::max())) > 0) {
          result += min_capacity;
        }
      }
      return result;
    }
  };
}

#endif

// main.cpp

int main() {
  std::int_fast64_t N;
  std::cin >> N;
  std::vector<tools::vector2<std::int_fast64_t>> red;
  red.reserve(N);
  std::copy_n(std::istream_iterator<tools::vector2<std::int_fast64_t>>(std::cin), N, std::back_inserter(red));
  std::vector<tools::vector2<std::int_fast64_t>> blue;
  blue.reserve(N);
  std::copy_n(std::istream_iterator<tools::vector2<std::int_fast64_t>>(std::cin), N, std::back_inserter(blue));

  tools::dinic<std::int_fast64_t> dinic(2 * N + 2);
  for (std::int_fast64_t i = 0; i < N; ++i) {
    dinic.add_edge(2 * N, i, 1);
    dinic.add_edge(N + i, 2 * N + 1, 1);
    for (std::int_fast64_t j = 0; j < N; ++j) {
      if (red[i].x < blue[j].x && red[i].y < blue[j].y) dinic.add_edge(i, N + j, 1);
    }
  }

  std::cout << dinic.calc(2 * N, 2 * N + 1) << std::endl;
  return 0;
}

Measured memory_bytes: 640