unittest
{
	assert( [ "3", "2 3", "1 3", "1 2" ].parse.expand.solve == 3 );
	assert( [ "4", "2 3 4", "1 3 4", "4 1 2", "3 1 2" ].parse.expand.solve == 4 );
	assert( [ "3", "2 3", "3 1", "1 2" ].parse.expand.solve == -1 );
}



import std.algorithm;
import std.container.binaryheap;
import std.conv;
import std.range;
import std.stdio;
import std.typecons;

void main()
{
	stdin.byLineCopy.parse.expand.solve.writeln;
}

auto parse( Range )( Range input )
if( isInputRange!Range && is( ElementType!Range == string ) )
{
	auto n = input.front.to!( long );
	auto ass = new long[][]( n, n - 1 );
	foreach( ref as; ass )
	{
		input.popFront;
		as = input.front.split.to!( long[] );
		as[] -= 1;
	}
	return tuple( ass );
}

auto solve( long[][] ass )
{
	auto ans = 0L;
	auto ct = 0L;
	auto cs = new long[ ass.length ];
	auto ds = new long[ ass.length ];
	auto queue = heapify!( ( a, b ) => b[ 0 ] < a[ 0 ] )( new Tuple!( long, long, long )[ ass.length ], 0 );
	foreach( long i, as; ass )
	{
		auto v = ass[ i ][ cs[ i ] ];
		queue.insert( tuple( cs[ i ], min( i, v ), max( i, v ) ) );
	}
	
	auto cg = ass.length * ass[ 0 ].length / 2;
	while( !queue.empty )
	{
		auto c = queue.front[ 0 ];
		auto i = queue.front[ 1 ];
		auto v = queue.front[ 2 ];
		queue.popFront;
		
		if( ass[ i ].length == cs[ i ] || ass[ v ].length == cs[ v ] ) continue;
		if( ass[ i ][ cs[ i ] ] != v   || ass[ v ][ cs[ v ] ] != i   ) continue;
		
		ans += ( ds[ i ] == ans || ds[ v ] == ans );
		ct++;
		cs[ i ]++;
		cs[ v ]++;
		ds[ i ] = ans;
		ds[ v ] = ans;
		
		if( cs[ i ] < ass[ i ].length ) queue.insert( tuple( cs[ i ], min( i, ass[ i ][ cs[ i ] ] ), max( i, ass[ i ][ cs[ i ] ] ) ) );
		if( cs[ v ] < ass[ v ].length ) queue.insert( tuple( cs[ v ], min( v, ass[ v ][ cs[ v ] ] ), max( v, ass[ v ][ cs[ v ] ] ) ) );
	}
	return ( ct == ass.length * ass[ 0 ].length / 2 ) ? ans : -1;
}
