// Arup Guha
// Started 10/5/2019, finished 10/6/2019
// Solution to 2019 NAQ Problem H: Running Routes

import java.util.*;
import java.io.*;

public class h {

	public static int n;
	public static ArrayList[] edges;
	public static int[][] memo;
		
	public static void main(String[] args) throws Exception {
	
		// Read stuff in.
		BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
		n = Integer.parseInt(stdin.readLine())+2;
		edges = new ArrayList[n];
		for (int i=0; i<n; i++) edges[i] = new ArrayList<Integer>();
		ArrayList<Integer> eList = new ArrayList<Integer>();
		for (int i=0; i<n-2; i++) {
			StringTokenizer tok = new StringTokenizer(stdin.readLine());
			for (int j=0; j<n-2; j++) {
				int item = Integer.parseInt(tok.nextToken());
				if (item == 1) {
					edges[i+1].add(j+1);
					if (j > i) eList.add((i+1)*(n)+(j+1));
				}
			}
		}
		
		// Array to speed up recursion (memoization).
		memo = new int[n][n];
		for (int i=0; i<n; i++) Arrays.fill(memo[i], -1);
		
		int res = 0;
		
		// Try each edge as an initial split for the polygon.
		for (Integer e: eList) {
			
			// Set up the two recursive calls.
			int u = e/n;
			int v = e%n;
			int left = v-u>3 ? go(u+1,v-1, n) : 0;
			int right = n-(v-u)>3 ? go((v+1+n)%n,(u-1+n)%n, n) : 0;
			
			// Update if we need to.
			res = Math.max(res, 1+left+right);
		}
		
		// Ta da!
		System.out.println(res);
	}
	
	// s == e means empty. Returns answer for vertices s to e, n is size.
	public static int go(int s, int e, int n) {
		
		// Easy base case.
		if (s==e) return 0;
		
		// Just wanted to cut off this case.
		if (s == e-1 || (s==n-1&&e==0)) {
			if (edges[s].contains(e)) return 1;
			return 0;
		}
		
		// Did this already, return the answer.
		if (memo[s][e] != -1) return memo[s][e];
		
		int res = 0;
		
		// Going forward...
		if (s < e) {
			
			
			res = 0;
			
			// take case edge from s to something.
			for (Integer x: (ArrayList<Integer>)edges[s]) {
				if (x > s && x <= e) {
					int left = s+1 < x-1 ? go(s+1, x-1, n) : 0;
					int right = x+1 < e ? go(x+1, e, n) : 0;
					int tmp = 1 + left + right;
					res = Math.max(res, tmp);
				}
			}
			
			// This means we take no edge including s.
			if (s+1<e) res = Math.max(res,go(s+1, e, n));
			
		}
		
		// s > e
		else {	
			res = 0;
			
			// take s to x.
			for (Integer x: (ArrayList<Integer>)edges[s]) {
				
				// Only do this if x is in between s and e.
				if (revIn(s,x,e)) {
					
					// Going left.
					int left = 0;
					if (x-s>2) left = go(s+1,x-1,n);
					else if (s<n-1&&x>0&&x<=e) left = go(s+1,x-1,n);
					else if (s==n-1&&x<=e&&x>0) left = go(0,x-1,n);
					
					// Going right.
					int right = 0;
					if (x+1<e||(x>s&&x<n-1)) right = go(x+1,e,n);
					else if (x==n-1&&e>0) right = go(0,e,n);
					
					// Update...
					int tmp = 1 + left + right;
					res = Math.max(res, tmp);
				}
			}
			
			// Don't include s...
			if (s<n-1) res = Math.max(res, go(s+1,e,n));
			else if (e>0) res = Math.max(res, go(0,e,n));
		}
		
		return memo[s][e] = res;
	}
	
	public static boolean revIn(int s, int x, int e) {
		if (x > s) return true;
		if (x <= e) return true;
		return false;
	}
	
}