import scala.annotation.tailrec
import scala.collection.mutable
object Main extends App {
  import scala.io.StdIn._

  val n = readInt()
  val blocks = Array.fill(n){
    readLine.trim.split(' ').map(_.toInt) match {
      case Array(w, h) ⇒ Block(w, h)
      case _ ⇒ ???
    }
  }
  val (nodes, source, sink) = makeNode(blocks)
  println(n - maxFlow(nodes, source, sink))
  def makeNode(blocks: Array[Block]): (Array[List[Flow]], Int, Int) = {
    val source = blocks.length * 2
    val sink = source + 1
    val nodes = Array.fill(sink + 1) {Nil: List[Flow]}
    for (i ← blocks.indices; j ← blocks.indices; if i != j) {
      if (blocks(i).canPutOn(blocks(j))) {
        addEdge(nodes, i * 2, j * 2 + 1)
      }
    }
    for (i ← blocks.indices) {
      addEdge(nodes, source, i * 2)
      addEdge(nodes, i * 2 + 1, sink)
    }
    (nodes, source, sink)
  }
  def maxFlow(nodes: Array[List[Flow]], source: Int, sink: Int): Int = {
    val depth = Array.fill(nodes.length){0}
    val queue = mutable.Queue[Int]()
    def setDepth(): Unit = {
      depth.indices.foreach(i ⇒ depth(i) = -1)
      depth(source) = 0
      queue.enqueue(source)
      while (queue.nonEmpty) {
        val current = queue.dequeue()
        for (Flow(to, hasFlow) ← nodes(current) if hasFlow && depth(to) == -1) {
          depth(to) = depth(current) + 1
          queue.enqueue(to)
        }
      }
    }
    val searching = nodes.clone()
    def dfs(current: Int): Boolean = {
      if (current == sink) return true
      else {
        while (true) {
          searching(current) match {
            case Nil ⇒ return false
            case (f@Flow(to, true))::rest if depth(current) < depth(to) && dfs(to) ⇒
              f.hasFlow = false
              f.pair.hasFlow = true
              return true
            case _::rest ⇒ searching(current) = rest
          }
        }
        false
      }
    }
    var count = 0
    var hasFlow = true
    while(hasFlow) {
      hasFlow = false
      setDepth()
      searching.indices.foreach(i ⇒ searching(i) = nodes(i))
      while(dfs(source)) {
        hasFlow = true
        count += 1
      }
    }
    count
  }
  class Block private (val height: Int, val width: Int) {
    def canPutOn(that: Block): Boolean = height < that.height && width < that.width
  }
  object Block {
    def apply(height: Int, width: Int) : Block = new Block(math.min(height, width), math.max(height, width))
  }
  def addEdge(nodes: Array[List[Flow]], from: Int, to: Int): Unit = {
    nodes(from) ::= Flow(to, hasFlow = true)
    nodes(to) ::= Flow(from, hasFlow = false)
    nodes(from).head.pair = nodes(to).head
    nodes(to).head.pair = nodes(from).head
  }
  case class Flow(to: Int, var hasFlow: Boolean) {
    private var mPair: Option[Flow] = None
    def pair: Flow = mPair.get
    def pair_=(that: Flow): Unit = mPair = Some(that)
  }
}
