#include <bits/stdc++.h>
#define FOR(i,b,e) for (common_type_t<decltype(b),decltype(e)> i = (b), i ## __end = (e); i < i ## __end; ++ i)
#define rep(i,n)   FOR (i, size_t{}, n)
#define ALL(x)     begin (x), end (x)
using namespace std;
using ll = int64_t;
constexpr ll inf = 2e18;

using vertex_t = size_t;
constexpr vertex_t invalid = ~ 0ull;

inline auto to_vertex (size_t i, size_t j)
{
  if (i > j) swap (i, j);
  return i * 1000 + j;
}

inline auto vertex_to_pair (vertex_t v)
{
  return pair <size_t, size_t> (v / 1000, v % 1000);
}

template <typename Graph>
inline auto topological_sort (const Graph & graph, const set <vertex_t> & v)
{
  //using vertex_t = size_t;
  enum class flag_t : unsigned char { YET, VISITED, DONE };
  auto n = graph.size ();
  vector <flag_t> visited (n, flag_t::YET);
  vector <vertex_t> res;
  auto res_ite = back_inserter (res);
  auto dfs = [&] (auto self, auto && from) {
    if (visited[from] == flag_t::DONE) return;
    if (visited[from] == flag_t::VISITED) throw runtime_error ("Error in topological_sort: The graph must be DAG.");
    visited[from] = flag_t::VISITED;
    for (auto && to : graph[from])
    {
      self (self, to);
    }
    * res_ite ++ = from;
    visited[from] = flag_t::DONE;
  };
  for (auto && i : v) dfs (dfs, i);
  reverse (ALL (res));
  return res;
}

template <typename Graph, typename Iterator>
inline auto solve (const Graph & graph, Iterator first, Iterator last)
{
  enum class flag_t : unsigned char { YET, VISITED, DONE };
  auto n = graph.size ();
  vector <flag_t> visited (n, flag_t::YET);
  vector <vertex_t> memo (n);
  ll res = 0;
  auto dfs = [&] (auto self, auto && from) -> ll {
    if (visited[from] == flag_t::DONE) return memo[from];
    if (visited[from] == flag_t::VISITED) throw runtime_error ("Error in topological_sort: The graph must be DAG.");
    visited[from] = flag_t::VISITED;
    ll ans = 1;
    for (auto && to : graph[from])
    {
      ans = max (ans, self (self, to) + 1);
    }
    visited[from] = flag_t::DONE;
    return memo[from] = ans;
  };
  FOR (ite, first, last)
  {
    res = max (res, dfs (dfs, * ite));
  }
  return res;
}

auto main () -> int
{
  cin.tie (nullptr);
  ios::sync_with_stdio (false);

  size_t n;
  cin >> n;
  vector <vector <size_t>> A (n);

  vector <vector <vertex_t>> g (1000 * 1000 + 1);

  rep (i, n)
  {
    auto pre_v = invalid;
    rep (j ,n - 1)
    {
      size_t a;
      cin >> a;
      -- a;
      A [i].push_back (a);
      if (pre_v != invalid)
      {
        g [pre_v].push_back (to_vertex (i, a));
      }
      pre_v = to_vertex (i, a);
    }
  }

  set <vertex_t> v;
  rep (j, n) rep (i, j) v.insert (to_vertex (i, j));
  try
  {
    vector <bool> battled (n);
    auto sorted = topological_sort (g, v);
    auto ans = solve (g, ALL (sorted));
    /*
    for (auto ite = begin (sorted); ite != end (sorted); )
    {
      size_t i, j;
      tie (i, j) = vertex_to_pair (* ite);
      if (battled [i] || battled [j]) {
        ++ ans;
        fill (ALL (battled), false);
      }
      else
      {
        battled [i] = battled [j] = true;
        ++ ite;
      }
    }
    if (any_of (ALL (battled), [&](auto && elem){return elem;})) ++ ans;
    */
#ifdef LOCAL
    cerr << "debug:" << endl;
    for (auto && elem : sorted)
    {
      size_t i, j;
      tie (i, j) = vertex_to_pair (elem);
      cout << "(" << i << ", " << j << ")" << endl;
    }
#endif
    cout << ans << endl;
  }
  catch (...)
  {
    cout << -1 << endl;
  }
}


Measured memory_bytes: 83612