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.container;
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 d = 0L;
	auto ds = new long[ ass.length ];
	auto ct = 0L;
	auto cg = ass.length * ass[ 0 ].length / 2;
	
	auto queue = DList!( Tuple!( long, long ) ).init;
	for( auto i = 0L; i < ass.length; i++ )
	{
		queue.insertBack( tuple( i, ass[ i ].front ) );
	}
	queue.insertBack( tuple( -1L, -1L ) );
	
	while( true )
	{
		auto i = queue.front[ 0 ];
		auto v = queue.front[ 1 ];
		queue.removeFront;
		
		// 1日の終わり
		if( i < 0 && v < 0 )
		{
			if( queue.empty ) break;
			d++;
			queue.insertBack( tuple( -1L, -1L ) );
			continue;
		}
		
		// 終わった試合orまだできない試合なのでスキップ
		if( ass[ i ].empty || ass[ v ].empty || ass[ i ].front != v || ass[ v ].front != i ) continue;
		
		// この日は既に試合しているので次の日に回す
		if( ds[ i ] == d || ds[ v ] == d )
		{
			queue.insertBack( tuple( i, v ) );
			continue;
		}
		
		// 試合実施
		ass[ i ].popFront;
		ass[ v ].popFront;
		if( !ass[ i ].empty ) queue.insertBack( tuple( i, ass[ i ].front ) );
		if( !ass[ v ].empty ) queue.insertBack( tuple( v, ass[ v ].front ) );
		ds[ i ] = d;
		ds[ v ] = d;
		ct++;
	}
	
	return ( ct == cg ) ? d : -1;
}
