use std::collections::BinaryHeap;

#[allow(unused)]
macro_rules! debug {
    ($($format:tt)*) => (write!(std::io::stderr(), $($format)*).unwrap());
}
#[allow(unused)]
macro_rules! debugln {
    ($($format:tt)*) => (writeln!(std::io::stderr(), $($format)*).unwrap());
}

fn main() {
    // 1つの数字
    let s = {
        let mut s = String::new();
        std::io::stdin().read_line(&mut s).unwrap();
        s.trim_right().to_owned()
    };
    let n: usize = s.parse().unwrap();

    // 1行ベクトル
    let mut graph_mat = vec![];
    for _ in 0..n {
        let s = {
            let mut s = String::new();
            std::io::stdin().read_line(&mut s).unwrap();
            s.trim_right().to_owned()
        };
        let v: Vec<usize> = s.split("").filter(|&x| x != "").map(|x| x.parse().unwrap()).collect();
        graph_mat.push(v);
    }
    // debugln!("{:?}", graph_mat);

    // 配列形式からリスト形式に変換
    let mut graph_list = vec![vec![]; n];
    for i in 0..n {
        for j in 0..n {
            if graph_mat[i][j] == 1 {
                graph_list[i].push(j);
            }
        }
    }
    // debugln!("{:?}", graph_list);

    // 2部グラフかどうか判定
    // if 2部グラフなら、グラフの直径を求める
    if is_bipartite_graph(&graph_list) {
        // let d = graph_diameter(&graph_list);
        let d = graph_diameter_wf(&graph_mat);
        println!("{}", d + 1);
    } else {
        println!("-1");
    }
}

// 2部グラフか判定
fn is_bipartite_graph(graph_list: &Vec<Vec<usize>>) -> bool {
    let n = graph_list.len();

    let mut stack: Vec<(usize, bool)> = vec![];
    let mut colors = vec![None; n];

    let start = 0;
    stack.push((start, true));

    while !stack.is_empty() {
        let (vertex, color) = stack.pop().unwrap();

        colors[vertex] = Some(color);

        for &next in &graph_list[vertex] {
            match colors[next] {
                Some(next_color) => {
                    if color == next_color {
                        return false;
                    }
                },
                None => stack.push((next, !color)),
            }
        }
    }

    true
}

#[allow(unused)]
fn graph_diameter_wf(ref graph_mat: &Vec<Vec<usize>>) -> usize {
    let d_mat = warshall_floyd(&graph_mat);
    let mut diameter = 0;
    for v in d_mat {
        for d in v {
            if d > diameter {
                diameter = d;
            }
        }
    }

    diameter
}

#[allow(unused)]
fn warshall_floyd(ref graph_mat: &Vec<Vec<usize>>) -> Vec<Vec<usize>> {
    let n = graph_mat.len();

    let mut d = vec![vec![std::usize::MAX >> 2; n]; n];
    for i in 0..n {
        for j in 0..n {
            if graph_mat[i][j] == 1 {
                d[i][j] = 1;
            }
        }
    }
    for i in 0..n {
        d[i][i] = 0;
    }

    for k in 0..n {
        for i in 0..n {
            for j in 0..n {
                if d[i][j] > d[i][k] + d[k][j] {
                    d[i][j] = d[i][k] + d[k][j];
                }
                // debugln!("i = {}, j = {}, k = {}, {:?}", i, j, k, d);
            }
        }
    }

    d
}

// グラフの直径
#[allow(unused)]
fn graph_diameter(graph_list: &Vec<Vec<usize>>) -> usize {
    let start = 0;
    let d_v = dijkstra(start, graph_list);
    // debugln!("{:?}", d_v);

    let mut farthest = start;
    let mut d_max = 0;
    for (v, &d) in d_v.iter().enumerate() {
        if d > d_max {
            d_max = d;
            farthest = v;
        }
    }

    let start = farthest;
    let mut d_max = 0;
    let d_v = dijkstra(start, graph_list);
    for (_, &d) in d_v.iter().enumerate() {
        if d > d_max {
            d_max = d;
        }
    }
    d_max
}

#[derive(PartialEq, Eq, Debug)]
struct Reverse<T> (T);

impl<T: Ord> Ord for Reverse<T> {
    fn cmp(&self, other: &Reverse<T>) -> std::cmp::Ordering {
        self.0.cmp(&other.0).reverse()
    }
}

impl<T: PartialOrd> PartialOrd for Reverse<T> {
    fn partial_cmp(&self, other: &Reverse<T>) -> Option<std::cmp::Ordering> {
        self.0.partial_cmp(&other.0).map(|a| a.reverse())
    }
}

// ダイクストラ
#[allow(unused)]
fn dijkstra(start: usize, graph_list: &Vec<Vec<usize>>) -> Vec<usize> {
    let n = graph_list.len();
    let mut distances = vec![std::usize::MAX >> 2; n];
    distances[start] = 0;

    // (distance, distination)
    let mut queue: BinaryHeap<Reverse<(usize, usize)>> = BinaryHeap::new();
    for vertex in 0..n {
        queue.push(Reverse((distances[vertex], vertex)));
    }

    while !queue.is_empty() {
        let Reverse((d, u)) = queue.pop().unwrap();
        // debugln!("u: {}", u);
        // debugln!("d: {}", d);
        // debugln!("queue: {:?}", queue);
        // debugln!("distances: {:?}", distances);
        for v in &graph_list[u] {
            // debugln!("v: {}", v);
            let alt = d + 1;
            if distances[*v] > alt {
                distances[*v] = alt;
                queue.push(Reverse((alt, *v)))
            }
        }
    }

    distances
}


Measured memory_bytes: 4352