#include <bits/stdc++.h>
#ifdef __LOCAL
    #define DBG(X) cout << #X << " = " << (X) << endl;
    #define SAY(X) cout << (X) << endl;
#else
    #define DBG(X)
    #define SAY(X)
#endif

#ifdef __LOCAL
    #include <filesystem>
    namespace fs = std::filesystem;
#endif

using namespace std;
using ll = long long int;
using pii = pair<int, int>;

inline void fast_io() { ios_base::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); };
template<typename T, typename S> inline ostream& operator<<(ostream& os, const pair<T, S> p) { cout << "[" << p.first << ";" << p.second << "]"; return os; }
template<typename T, typename S> inline ostream& operator<<(ostream& os, const map<T, S> p) { for (auto el : p) cout << "[" << el.first << ";" << el.second << "]"; return os; }
template<typename T> inline ostream& operator<<(ostream& os, const vector<T>& v) { for (auto el : v) cout << el << " "; return os; }
template<typename T> inline vector<T> fetch_vec(int sz) { vector<T> ret(sz); for (auto& elem : ret) cin >> elem; return ret; }

ll N,V;
vector<vector<int>> A;
vector<vector<int>> G;
vector<int> node;

void input(){
    #ifdef __LOCAL
        fs::path p = __FILE__;
        fs::path input,output;
        input = output = p.parent_path();
        input += string("/input/") + string(p.stem()) + string(".txt");
        output += string("/output/") + string(p.stem()) + string(".txt");
        freopen(input.c_str(), "r", stdin);
        freopen(output.c_str(), "w", stdout);
    #endif

    fast_io();
    cin >> N;
    A.assign(N,vector<int>());
    for (int i = 0; i < N; i++)
        for (int j = 0; j < N-1; j++){
            int a;
            cin >> a;
            A[i].push_back(--a);
        }

    V=N*N;
    G.resize(V);
    for (int i = 0; i < N; i++)
        for (int j = 0; j < N-2; j++){
            int x=min(i,A[i][j]);
            int y=max(i,A[i][j]);
            int z=min(i,A[i][j+1]);
            int w=max(i,A[i][j+1]);
            int X=N*x+y;
            int Z=N*z+w;
            node.push_back(X);
            G[X].push_back(Z);
        }
}

// TLEでテストケースが通らない。
// 最悪でO(N^3)ぐらいある。
// 愚直にやるんでなくて、工夫する必要がある。
// int TLEsolve(){
//     ll day=0;
//     vector<bool> match(N,false);
//     vector<bool> emptys(N,false);
//     ll emptyNum=0;
//     while(day<N*N/2){
//         bool ismatched=false;
//         match.assign(N,false);
//         for(ll i=0;i<N;i++){
//             if(match[i]) continue;
//             if(A[i].empty()) {if(!emptys[i])emptyNum+=i; emptys[i]=true; continue;}
//             ll x = A[i].front();
//             if(match[x]) continue;
//             if(A[x].empty()) {if(!emptys[x])emptyNum+=x; emptys[x]=true; continue;}
//             ll y = A[x].front();
//             if(y==i && !match[x] && !match[y]){
//                 match[x] = match[y] = true;
//                 A[x].pop_front();
//                 A[y].pop_front();
//                 ismatched=true;
//                 if(A[x].empty()) {if(!emptys[x])emptyNum+=x; emptys[x]=true;}
//                 if(A[y].empty()) {if(!emptys[y])emptyNum+=y; emptys[y]=true;}
//             }
//         }
//         day++;
//         // bool chk=true;
//         // REP(i,N) chk &= A[i].empty();
//         if(2LL*emptyNum==(N-1)*N) {cout << day << endl; return 0;}
//         if(!ismatched){
//             cout << "-1" << endl;
//             return 0;
//         }
//     }
//     bool ans=true;
//     REP(i,N) ans &= A[i].empty();
//     if(ans) cout << day << endl;
//     else cout << "-1" << endl;
//     return 0;
// }

// map<pii,vector<pii>> G;
// vector<vector<vector<pii>>> G;

bool isCyclicUtil(int v, vector<bool> &visited, vector<bool> &recStack) 
{ 
    if(visited[v] == false) 
    { 
        // Mark the current node as visited and part of recursion stack 
        visited[v] = true; 
        recStack[v] = true; 
  
        // Recur for all the vertices adjacent to this vertex 
        for(auto u:G[v]) 
        { 
            if ( !visited[u] && isCyclicUtil(u, visited, recStack) ) 
                return true; 
            else if (recStack[u]) 
                return true; 
        } 
  
    } 
    recStack[v] = false;  // remove the vertex from recursion stack 
    return false; 
} 

bool isCyclic() 
{ 
    // Mark all the vertices as not visited and not part of recursion 
    // stack 
    // map<pii,bool> visited,recStack;
    vector<bool> visited(V),recStack(V);
    // for(int i = 0; i < V; i++) 
    // { 
    //     visited[i] = false; 
    //     recStack[i] = false; 
    // } 
  
    // Call the recursive helper function to detect cycle in different 
    // DFS trees 
    // for(int i = 0; i < V; i++) 
    for(auto u:node) 
        if (isCyclicUtil(u, visited, recStack)) 
            return true; 
  
    return false; 
} 

vector<int> dp;
vector<bool> used;
int dfs(int s){
    used[s]=true;
    if(dp[s]!=0) return dp[s];
    dp[s]=1;
    for(auto u:G[s]){
        int res = dfs(u);
        dp[s] = max(dp[s],res+1);
    }
    return dp[s];
}

// dagを使う。
// 閉路検出と最長経路を求める問題に帰着させる。
int solve(){

    if(isCyclic()) {cout << -1 << endl; return 0;}
    int ans=0;
    used.resize(V);
    dp.resize(V);
    for (int i = 0; i < N; i++)
    {
        // dp.clear();
        int x=min(i,A[i][0]);
        int y=max(i,A[i][0]);
        int X=N*x+y;
        if(used[X]) continue;
        ans = max(ans, dfs(X));        
    }
    
    cout << ans << endl;
    return 0;
}


int main()
{
    input();
    solve();
    return 0;
}


Measured memory_bytes: 51564