#include <bits/stdc++.h>

using ll = long long;
using namespace std;

#define swap(a, b)    do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while(false)
#define repd(i, a, b) for(typeof(b) i = a; i < (b); ++i)
#define rep(i, n)     repd(i, 0, n)
#define dump(x)       cerr << #x << " = " << (x) << endl
#define debug(x)      cerr << "L" << __LINE__ << ": " << #x << " = " << (x) << endl
#define YN(x)         cout << ((x) ? "YES" : "NO") << endl
#define Yn(x)         cout << ((x) ? "Yes" : "No") << endl
#define yn(x)         cout << ((x) ? "yes" : "no") << endl
#define absi(x)       ( ((x) >= 0) ? (x) : (-(x)) )
#define debugvec(v)   rep(i, v.size()) cerr << "L" << __LINE__ << ": " << #v << "[" << i << "] = " << v[i] << endl;
#define debugvec2(v)  cerr << "L" << __LINE__ << ": " << #v << " = { "; rep(i, v.size()) cerr << v[i] << (i + 1 == v.size() ? "" : ", "); cerr << " }" << endl;

ll gcd(ll a, ll b) { return __gcd(a, b); }
ll lcm(ll a, ll b) { return (a == 0 || b == 0) ? 0 : (a / __gcd(a, b) * b); }

class directedgraph {
private:
	vector<vector<ll>> g;
public:
	directedgraph(const ll n) {
		g = vector<vector<ll>>(n);
	}
	
	bool add_edge(const ll tail, const ll head, const bool check_multiple_edges = false) {
		if( !check_multiple_edges ) {
			g.at(tail).push_back(head);
			return true;
		}

		if(has_edge(tail, head)) {
			return false;
		}
		g.at(tail).push_back(head);
		return true;
	}

	vector<ll> vertices_connected_with(const ll vertex) const {
		return g.at(vertex);
	}

	bool has_edge(const ll tail, const ll head) const {
		for(auto itr = g.at(tail).begin(); itr != g.at(tail).end(); ++itr ) {
			if(*itr == head) return true;
		}
		return false;
	}

	bool has_path(const ll from, const ll to) const {
		return distance(from, to) >= 0;
	}

	bool is_acyclic() const {
		const auto v = topological_sort();
		return !v.empty();
	}

	bool is_cyclic() const {
		return !is_acyclic();
	}

	/* 最短距離を BFS で探索 */
	ll distance(const ll from, const ll to) const {
		if(from == to) return 0;

		vector<ll> d(g.size(), -1); // d[i] = from から i までの距離。-1 の場合はまだ不明
		d.at(from) = 0;

		queue<ll> q;
		q.push(from);

		while(!q.empty()) {
			ll current = q.front();
			q.pop();

			for(auto itr = g.at(current).begin(); itr != g.at(current).end(); ++itr) {
				if(*itr == to) return d.at(current) + 1;

				if(d.at(*itr) < 0) {
					d.at(*itr) = d.at(current) + 1;
					q.push(*itr);
				}
			}
		}

		return -1;
	}

	/*
	 *  多分バグっていない
	 *  これは通った：https://atcoder.jp/contests/nikkei2019-qual/tasks/nikkei2019_qual_d
	 */
	vector<ll> topological_sort() const {
		/*
		vector<vector<ll>> g_rev(g.size()); // g を逆向きに表現したもの。g_rev[i][hoge] = j : j -> i の Edge
		rep(from, g.size()) {
			for(ll to : g.at(from)) {
				g_rev.at(to).push_back(from);
				indegree.at(to) += 1;
			}
		}
		
		vector<ll> indegree(g.size(), 0);   // indegree[i] = i の入次数
		rep(i, n) {
			indegree.at(i) = g_rev.at(i).size();
		}
		*/

		vector<ll> indegree(g.size(), 0); // indegree[i] = i の入次数
		for(auto itrv = g.begin(); itrv != g.end(); ++itrv) {
			for(auto itr = (*itrv).begin(); itr != (*itrv).end(); ++itr) {
				indegree.at(*itr) += 1;
			}
		}

		queue<ll> q;                    // BFS に使用する queue
		//vector<ll> order(g.size(), -1); // Topological Sort した時、Vertex i が order[i] 番目の序列に属する
		rep(i, g.size()) {
			if(indegree.at(i) == 0) {
				q.push(i);
				//order.at(i) = 0;
			}
		}

		/* Edge が刺さっていない Vertex が存在しなければ、Cyclic */
		if( q.empty() ) {
			return vector<ll>(0);
		}

		/* BFS */
		vector<ll> ret(0);
		ret.reserve(g.size());
		while( !q.empty() ) {
			ll current = q.front(); q.pop();
			ret.push_back(current);
			for(const auto& current_to: g.at(current)) {
				indegree.at(current_to) -= 1;
				if(indegree.at(current_to) == 0) {
					q.push(current_to);
				}
			}
		}

		/* Edge が残っていれば、Cyclic */
		if( *max_element(indegree.begin(), indegree.end()) > 0 ) {
			return vector<ll>(0);
		}

		return ret;
	}

	/* バグっている */
	vector<vector<ll>> topological_sort_extended() const {
		auto ts = topological_sort();
		if(ts.empty()) return vector<vector<ll>>(0);

		vector<ll> order(g.size(), 0);
		for(const auto& i : ts) {
			for(auto itr = g.at(i).begin(); itr != g.at(i).end(); ++itr) {
				order.at(*itr) = std::max(order.at(i) + 1, order.at(*itr));
			}
		}

		vector<vector<ll>> ret( order.at(ts.back()) + 1 );
		rep(i, ts.size()) {
			ret.at(order.at(i)).push_back(i);
		}

		debug(ret.size());
		return ret;
	}
};
ll game_number(ll a, ll b, ll n) {
	if(a > b) swap(a, b);
	return a * n - a * (a + 1) / 2 + b - a - 1;
}

int main(int argc, const char* argv[]) {
	ll n;
	cin >> n;
	vector<vector<ll>> a(n, vector<ll>(n - 1, 0));
	directedgraph g( (n * n - n) / 2 );

	rep(i, n) {
		rep(j, n - 1) {
			cin >> a[i][j];
			--a[i][j];
		}
	}

	vector<vector<ll>> b(n, vector<ll>(n, 0)); // 試合をノードの番号にしたやつ
	rep(i, n) {
		rep(j, n - 1) {
			ll player1 = i;
			ll player2 = a[i][j];
			b[i][j] = game_number(player1, player2, n);
		}
	}

	rep(i, n) {
		rep(j, n - 2) {
			g.add_edge(b[i][j], b[i][j+1]);
		}
	}

	auto ts = g.topological_sort_extended();
	ll ret = ts.size();
	if(ret == 0) ret = -1;

	for(const auto& x: ts) {
		for(const auto& y: x) {
			cerr << y << " ";
		}
		cerr << endl;
	}

	cout << ret << endl;

	return 0;
}


Measured memory_bytes: 78400