// Arup Guha
// Solution to 2012 NCPC Problem: Kindergarten
// 4/21/2019

import java.util.*;
import java.io.*;

public class kindergarten {

	public static int n;
	public static int[] myclass;
	public static int[][] ranks;
	
	public static void main(String[] args) throws Exception {

		BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
		n = Integer.parseInt(stdin.readLine().trim());
		
		myclass = new int[n];
		ranks = new int[n][n-1];
		for (int i=0; i<n; i++) {
			StringTokenizer tok = new StringTokenizer(stdin.readLine());
			myclass[i] = Integer.parseInt(tok.nextToken());
			
			for (int j=0; j<n-1; j++)
				ranks[i][j] = Integer.parseInt(tok.nextToken());
		}
		
		// Run a binary search on the minimum value.
		int low = 0, high = n-1;
		while (low < high) {
			int mid = (low+high)/2;
			boolean canDo = solve(mid);
			
			if (canDo)
				high = mid;
			else
				low = mid+1;
		}
		System.out.println(low);
	}
	
	public static boolean solve(int k) {
		
		// 3 variables for each student.
		TwoSat ts = new TwoSat(3*n);
		
		// For each student enforce their new classroom.
		for (int i=0; i<n; i++) {
			
			// Two opposite teachers are stored in a and b. Must be in exactly one of these.
			int a = 0, b = 1;
			if (myclass[i] == 0) a = 2;
			else if (myclass[i] == 1) b = 2;
			ts.addXor(true,3*i+a,true,3*i+b);
			
			// Can't be in the old class.
			ts.addForce(3*i+myclass[i], false);
			
			// I can't be in the same class as other people I don't like.
			for (int j=k; j<n-1; j++) {				
				ts.addImpl(true,3*i+a,false,3*(ranks[i][j]-1)+a );
				ts.addImpl(true,3*i+b,false,3*(ranks[i][j]-1)+b );
			}
		}
	
		// Ta da!
		return ts.solve();
	}
}

/*** UCF Hackpack Code for SCC and 2-SAT ***/
class SCC {
  ArrayList<Integer>[] adj;
  ArrayList<Integer>[] comp;
  int n;
  int idx, cs;
  boolean[] u;
  int[] pre, low, map;
  ArrayDeque<Integer> s;
  public SCC(int nn) {
    adj = new ArrayList[n = nn];
    for (int curr = 0; curr < n; ++curr)
      adj[curr] = new ArrayList<>();
  }
  void add(int v1, int v2) {
    adj[v1].add(v2); 
  }
  int[] go() {
  comp = new ArrayList[n];
    idx = 1; cs = 0;
    pre = new int[n]; low = new int[n]; map = new int[n];
    u = new boolean[n];
    s = new ArrayDeque<Integer>();
    for (int i = 0; i < n; ++i)
      if (pre[i] == 0)
        dfs(i);
    return map;
  }

  void dfs(int v) {
    pre[v] = low[v] = idx++;
    s.push(v);
    u[v] = true;
    for (int to : adj[v]) {
      if (pre[to] == 0) {
        dfs(to);
        low[v] = Math.min(low[v], low[to]);
      } else if (u[to]) {
        low[v] = Math.min(low[v], pre[to]);
      }
    }
    if (low[v] == pre[v]) {
      int next;
      comp[cs] = new ArrayList<>();
      do {
        next = s.pop();
        u[next] = false;
        map[next] = cs;
        comp[cs].add(next);
      } while (next != v);
      cs++;
    }
  }

  //# Make sure to call go() before calling this function.
  // Function returns a new graph such that all SCCs are
  // compressed into one node and only the edges connecting
  // two components are in the new graph.
  // nodeMapping is an integer array that will store the mapping
  // for each node in the old graph into the new graph.//$
  ArrayList[] compressSCC() {
    ArrayList<Integer>[] g = new ArrayList[cs];
    for(int i = 0; i < cs; i++) g[i] = new ArrayList<Integer>();
    int[] added = new int[cs];
    Arrays.fill(added, -1);
    for(int i = 0; i < cs; i++) {
      for(int v : comp[i]) {
        for(int to : adj[v]) {
          int mapTo = map[to];
          if(mapTo != i && added[mapTo] != i) {
            g[i].add(mapTo);
            added[mapTo] = i;
          }
        }
      }
    }
    return g;
  }
}

class TwoSat {
	int n, n2;  byte[] vals, cvals;  SCC scc;

	public TwoSat(int nn) {  n2 = 2 * (n = nn);  scc = new SCC(n2);  vals = new byte[n2];}

	// Example: (true, 0, false, 1) => 0 --> !1
	public void addImpl(boolean va, int a, boolean vb, int b) {
		a <<= 1; b <<= 1;  if (!va) a++;  if (!vb) b++;
		scc.add(a, b);  scc.add(b ^ 1, a ^ 1);
	}

	//# Add (a NAND b)
	public void addNand(boolean va, int a, boolean vb, int b) {
		addImpl(va, a, !vb, b);
	}//$

	//# Add (a OR b)
	public void addOr(boolean va, int a, boolean vb, int b) {
		addImpl(!va, a, vb, b); // ~a -> b
	}//$

	//# Add (a XOR b)
	public void addXor(boolean va, int a, boolean vb, int b) {
		addImpl(!va, a, vb, b); // ~a -> b
		addImpl(va, a, !vb, b); // a -> ~b
	}//$

	//# Forces a to have whatever value va is
	public void addForce(int a, boolean va) {
		addImpl(!va, a, va, a);
	}//$

	// Returns the value of the variable v
	public boolean getVal(int v) {
		return vals[v << 1] == 1;
	}

	public boolean solve() {
		int map[] = scc.go();
		for(int i = 0; i < n; i++) {
			if(map[2*i] == map[2*i+1]) return false;
		}
		for(int i = 0; i < n2; i++) vals[i] = (byte)-1;
		for(int i = 0; i < scc.cs; i++) {
			for(int v : scc.comp[i]) {
				if(vals[v] == -1) {
					vals[v] = 1;
				}
				vals[v^1] = (byte)(vals[v]^1);
			}
		}
		return true;
	}
}
