#include <bits/stdc++.h>

using namespace std;

struct Bipartite_Matching
{
  vector< vector< int > > graph;
  vector< int > match, alive, used;
  int timestamp = 0;

  Bipartite_Matching(int n)
  {
    graph.resize(n);
    alive.assign(n, 1);
    used.assign(n, 0);
    match.assign(n, -1);
  }

  void add_edge(int u, int v)
  {
    graph[u].push_back(v);
    graph[v].push_back(u);
  }

  bool dfs(int v)
  {
    used[v] = timestamp;
    for(int i = 0; i < graph[v].size(); i++) {
      int u = graph[v][i], w = match[u];
      if(alive[u] == 0) continue;
      if(w == -1 || (used[w] != timestamp && dfs(w))) {
        match[v] = u;
        match[u] = v;
        return (true);
      }
    }
    return (false);
  }

  int bipartite_matching()
  {
    int ret = 0;
    for(int i = 0; i < graph.size(); i++) {
      if(alive[i] == 0) continue;
      if(match[i] == -1) {
        ++timestamp;
        ret += dfs(i);
      }
    }
    return (ret);
  }

  int back(int k)
  {
    match[match[k]] = -1;
    match[k] = -1;
    ++timestamp;
    dfs(k);
    return (match[k]);
  }

  void decide(int k)
  {
    alive[match[k]] = 0;
    alive[k] = 0;
  }

};

int main()
{
  int N;
  scanf("%d", &N);
  --N;
  Bipartite_Matching flow(N + N);
  for(int i = 0; i < N; i++) {
    for(int j = 0; j < N; j++) {
      int k;
      scanf("%d", &k);
      if(k) flow.add_edge(j, N + i);
    }
  }
  auto get = flow.bipartite_matching();
  if(get < N) {
    cout << "no" << endl;
  } else {
    cout << "yes" << endl;
    for(int i = 0; i < N; i++) {
      printf("%d\n", flow.back(i) - N + 1);
      flow.decide(i);
    }
  }
}