#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
//typedef long double ld;

typedef map<int, int> mii;
typedef pair<int, int> pii;
typedef pair<double,double> pdd;
typedef set<int> si;
typedef set<ll> sll;
typedef vector<int> vi;
typedef vector<pair<int, int > > vii;
typedef vector<ll> vll;
typedef vector<double> vd;
typedef vector<vi> vvi;
typedef vector<vii> vvii;
typedef vector<vll> vvll;

constexpr ll MOD = 1e9+7;
constexpr int INF = 1<<30;
constexpr ll  LINF = 1LL<<60;

#define Sort(x) sort(x.begin(),x.end())
#define Reverse(x) reverse(x.begin(),x.end())
#define ABS(a,b) ((a)<(b)?(b)-(a):(a)-(b))
#define nbit(n) (1ll<<(n))

// sort(begin(x), end(x), greater<ll>());
// lower_bound(a.begin(), a.end(), val): a[i] ≥ valである最左の位置
// upper_bound(a.begin(), a.end(), val): a[i] > valである最左の位置

// Returns minimum of a and b.
// If a is less b, a is set to b.
template<typename T>
T chmin(T& a, T b) {
  if (a > b) {
    a = b;
  }
  return a;
}

// Returns maximum of a and b.
// If a is less b, a is set to b.
template<typename T>
T chmax(T& a, T b) {
  if (a < b) {
    a = b;
  }
  return a;
}

// a = (a+b)%MOD;
void addmod(ll& a, ll b) {
  a = (a+b)%MOD;
}

template <typename T>
void dumpContents(const T& v, const string& msg)
{
  cerr << "### " << msg << " ###\n";
  for (const auto& x : v) {
    cerr << x << " ";
  }
  cerr << endl;
}

struct before_main_function {
  before_main_function() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    cout<<setprecision(20)<<fixed;
    #define endl "\n"
  }
} before_main_function;

// ========== end of template ==========
// Sort DAG nodes in a topological order
struct TopologicalSort {

  struct Edge {int from, to; }; // 0-indexed

  struct Node {
    vector<int> to_nodes;
    int nin; // count of inbounds
  };

  int N; // number of nodes
  vector<Node> graph;
  vector<int> sorted; // a list of nodes IDs sorted topologically

  TopologicalSort(int _N, const vector<Edge>& edges)
    : N(_N) {
    graph.resize(N);
    sorted.reserve(N);

    for (auto e : edges) {
      graph[e.from].to_nodes.emplace_back(e.to);
      graph[e.to].nin++;
    }
  }

  // Return true if DAG (no cycle); otherwise, false (cycle)
  bool sort() {
    deque<int> S; // node IDs such that nid == 0

    for (int i=0; i<int(graph.size()); i++) {
       const auto& n = graph[i];
       if (n.nin > 0) continue;
       S.push_back(i);
    }

    while (!S.empty()) {
      const int p = S.back(); S.pop_back();
      sorted.emplace_back(p);
      for (const auto& to : graph[p].to_nodes) {
        graph[to].nin--;
	if (graph[to].nin == 0) {
	  S.push_back(to);
	}
      }
    }

    // No cycles
    return int(sorted.size()) == N;
  }

  int longestPath() {
    vi d(N);

    // Update in the topological order
    for (auto from : sorted) {
      for (auto next : graph[from].to_nodes) {
        d[next] = max({d[next], d[from] + 1});
      }
    }

    auto it = max_element(begin(d), end(d));
    return *it;
  }

};


constexpr int N_MAX = 1000;
int N;

int A[N_MAX][N_MAX] = {0};

int M[N_MAX][N_MAX] = {0};

int main(int argc, char** argv) {
  cin >> N;

  for (int i=0; i<N; i++) {
    for (int j=0; j<N-1; j++) {
      int x;
      cin >> x;
      x--; // to 0-indexed
      A[i][j] = x;
    }
  }

  int index = 0;
  for (int i=0; i<N; i++) {
    for (int j=i+1; j<N; j++) {
      M[i][j] = index;
      M[j][i] = index;
      index++;
    }
  }

  assert(index == N*(N-1)/2);

  // 試合がnode

  vector<TopologicalSort::Edge> edges;

  for (int i=0; i<N; i++) {
    for (int j=0; j<N-2; j++) {
      int fromNode = M[i][A[i][j]];
      int toNode   = M[i][A[i][j+1]];
      edges.push_back(TopologicalSort::Edge{fromNode, toNode});
    }
  }

  TopologicalSort ts(index, edges);
  bool ok = ts.sort();
  if (!ok) {
    cout << -1 << endl;
    return 0;
  }

  cout << ts.longestPath() + 1 << endl;

  return 0;
}


Measured memory_bytes: 51048