import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer

object Main extends App {

  import scala.io.StdIn._
  implicit class Extend[T](value: T){
    def let[R](func: T ⇒ R): R = func(value)
    def also(func: T ⇒ Unit): T = {func(value); value}
  }
  val n = readLine.trim.toInt
  val edges = makeGraph(n - 1, n - 1, Array.tabulate(n - 1){_ ⇒ readLine.trim.split(' ').map(_ == "1")}.let{arr ⇒ arr.indices.flatMap(f ⇒ arr(f).indices.collect{case t if arr(f)(t) ⇒ (t, f)}).toArray})
  val levels = Array.tabulate(n * 2){_ ⇒ -1}
  val dfsLast = Array.tabulate(n * 2){_ ⇒ 0}

  var matchingCount = 0
  var canMatch = true
  while (canMatch){
    canMatch = false
    bfs
    if (levels.last != -1) {
      canMatch = true
      while (dfs(0)) {
        matchingCount += 1
      }
    }
  }

  if (matchingCount == n - 1) {
    var matchingEdge = Nil:List[Int]
    for (x ← 1 until n){
      resetPath(x)
      levels.indices.foreach(i ⇒ levels(i) = -1)
      simpleDfs(0)
      matchingEdge ::= findMatching(x) - n + 1
      confirmMatching(x)
    }
    println("yes")
    println(matchingEdge.reverse.mkString("\n"))
  }else {
    println("no")
  }
  def findMatching(x: Int): Int = {
    edges(x).find(_._3 == false) match {
      case Some((to, _, _)) ⇒
        to
      case None ⇒ ???
    }
  }
  def confirmMatching(x: Int): Unit = {
    edges(x)(0) match {
      case (0, l, _) ⇒ edges(0)(l) = (x, 0, false)
      case _ ⇒ ???
    }
    edges(x).find(_._3 == false) match {
      case Some((y, l, _)) ⇒ edges(y)(l) = edges(y)(l).copy(_3 = false)
      case None ⇒ ???
    }

  }
  def resetPath(x: Int): Unit = {
    edges(x).indexWhere(_._3 == false) match {
      case i ⇒
        val (y, l, _) = edges(x)(i)
        edges(0)(edges(x)(0)._2) = edges(0)(edges(x)(0)._2).copy(_3 = true)
        edges(x)(i) = edges(x)(i).copy(_3 = true)
        edges(y)(l) = edges(y)(l).copy(_3 = false)
        edges(y)(0) = edges(y)(0).copy(_3 = true)
    }
  }
  def bfs: Unit = {
    dfsLast.indices.foreach(i ⇒ dfsLast(i) = 0)
    levels.indices.foreach(i ⇒ levels(i) = -1)
    levels(0) = 0
    val queue : mutable.Queue[Int]= mutable.Queue()
    queue.enqueue(0)
    while(queue.nonEmpty){
      val top = queue.dequeue()
      for ((to, _, b) ← edges(top) if b && levels(to) == -1) {
        levels(to) = levels(top) + 1
        queue.enqueue(to)
      }
    }
  }
  def simpleDfs(from: Int): Boolean = {
    if (from == n * 2 - 1) true
    else if (levels(from) != -1) false
    else {
      levels(from) = 0
      edges(from).zipWithIndex.find{case (it, _) ⇒ it._3 && simpleDfs(it._1)} match {
        case Some(((to, l, _), idx)) ⇒
          edges(from)(idx) = edges(from)(idx).copy(_3 = false)
          edges(to)(l) = edges(to)(l).copy(_3 = true)
          true
        case None ⇒ false
      }
    }
  }
  def dfs(from: Int): Boolean= {
    if (from == n * 2 - 1) true
    else {
      var makePath = false
      for (i ← dfsLast(from) until edges(from).length if  !makePath && edges(from)(i)._3) {
        if (levels(edges(from)(i)._1) > levels(from) && dfs(edges(from)(i)._1)) {
          val (to, j, _) = edges(from)(i)
          edges(from)(i) = (to, j, false)
          edges(to)(j) = (from, i, true)
          makePath = true
          dfsLast(from) = i + 1
        }
      }
      makePath
    }
  }

  def makeGraph(s: Int, t: Int, edge: Array[(Int, Int)]): Array[Array[(Int, Int, Boolean)]] = {
    val edges = Array.tabulate(s + t + 2){_ ⇒ ArrayBuffer[(Int, Int, Boolean)]()}
    for (x ← (1 to s).reverse){
      edges(x).append((0, edges(0).length, false))
      edges(0).append((x, 0, true))
    }
    for (y ← (s + 1) to s + t){
      edges(y).append((s + t + 1, edges(s + t + 1).length, true))
      edges(s + t + 1).append((y, 0, false))
    }
    for ((from, to) ← edge){
      edges(from + 1).append((to + s + 1, edges(to + s + 1).length, true))
      edges(to + s + 1).append((from + 1, edges(from + 1).length - 1, false))
    }
    edges.map{_.toArray}
  }
}
