#include <bits/stdc++.h>

using namespace std;

template<typename S, typename T>
ostream& operator<<(ostream& out, const pair<S, T> p) {
    out << "(" << p.first << "," << p.second << ")";
    return out;
}

template<typename T>
ostream& operator<<(ostream& out, const vector<T>& v) {
    for (auto a: v)
        out << a << " ";
    return out;
}

template<typename T>
ostream& operator<<(ostream& out, const set<T>& S) {
    for (auto a: S)
        cout << a << " ";
    return out;
}

template<typename T>
ostream& operator<<(ostream& out, const multiset<T>& S) {
    for (auto a: S)
        cout << a << " ";
    return out;
}

template<typename S, typename T>
ostream& operator<<(ostream& out, const map<S, T>& M) {
    for (auto m: M)
        cout << "(" << m.first << "->" << m.second << ") ";
    return out;
}

template<typename S, typename T>
pair<S, T> operator+(pair<S, T> a, pair<S, T> b) {
    return make_pair(a.first + b.first, a.second + b.second);
}

template<typename S, typename T>
pair<S, T> operator-(pair<S, T> a, pair<S, T> b) {
    return make_pair(a.first - b.first, a.second - b.second);
}

struct BicolorGraph {
    int n;
    vector<vector<int>> E;
    vector<int> vis;
    vector<vector<int>> sa;
    vector<vector<vector<int>>> group;
    bool is_bicolorable;

    void init(int n) {
        this->n = n;
        E.resize(n);
        for (int i = 0; i < n; i++)
            E[i].clear();
    }

    void add_undirected_edge(int s, int t) {
        E[s].push_back(t);
        E[t].push_back(s);
    }

    void refactor_edge() {
        for (int i = 0; i < n; i++) {
            sort(E[i].begin(), E[i].end());
            E[i].erase(unique(E[i].begin(), E[i].end()), E[i].end());
        }
    }

    int bicolor(int s) {
        for (auto t: E[s]) {
            if (!vis[t]) {
                vis[t] = 3 - vis[s];
                if (!bicolor(t))
                    return 0;
            } else if (vis[t] == vis[s]) {
                return 0;
            }
        }
        return 1;
    }


    void source_bicolor() {
        vis.assign(n, 0);
        is_bicolorable = true;
        for (int i = 0; i < n; i++) {
            if (!vis[i]) {
                vis[i] = 1;
                if (!bicolor(i)) {
                    is_bicolorable = false;
                    return;
                }
            }
        }
    }

    int bicolor_group(int s) {
        int ret = vis[s];
        for (auto t: E[s]) {
            if (!vis[t]) {
                vis[t] = 3 - vis[s];
                if (!bicolor_group(t))
                    ret = 0;
            } else if (vis[t] == vis[s]) {
                ret = 0;
            }
        }
        sa[vis[s]-1].push_back(s);
        return ret;
    }

    void source_bicolor_group() {
        vis.assign(n, 0);
        sa.resize(3);
        group.clear();
        is_bicolorable = true;
        for (int i = 0; i < n; i++) {
            if (!vis[i]) {
                for (int i = 0; i < sa.size(); i++)
                    sa[i].clear();
                vis[i] = 1;
                if (!bicolor_group(i)) {
                    is_bicolorable = false;
                    for (auto a: sa[0])
                        sa[2].push_back(a);
                    for (auto a: sa[1])
                        sa[2].push_back(a);
                    sa[0].clear();
                    sa[1].clear();
                }
                group.push_back(sa);
            }
        }
    }

    bool query_is_bicolorable() {
        return is_bicolorable;
    }

    vector<vector<int>> query_bicorable_group() {
        vector<vector<int>> ans(2);
        for (int i = 0; i < group.size(); i++) {
            for (int j = 0; j < 2; j++) {
                for (auto a: group[i][j]) {
                    ans[j].push_back(a);
                }
            }
        }
        return ans;
    }

    vector<int> query_monochromatic_dominating_set() {
        vector<int> ans;
        for (int i = 0; i < group.size(); i++) {
            if (group[i][2].empty()) {
                int j;
                if (group[i][0].empty())
                    j = 1;
                else if (group[i][1].empty())
                    j = 0;
                else if (group[i][0].size() <= group[i][1].size())
                    j = 0;
                else if (group[i][0].size() > group[i][1].size())
                    j = 1;
                for (auto a: group[i][j])
                    ans.push_back(a);
            }
        }
        return ans;
    }

    vector<vector<vector<int>>> query_raw_group() {
        return group;
    }

    void dump_edge() {
        cout << "n: " << n << endl;
        for (int i = 0; i < E.size(); i++)
            cout << "E[" << i << "]: " << E[i] << endl;
    }

    void dump() {
        cout << "is_bicolrable: " << is_bicolorable << endl;
        for (int i = 0; i < group.size(); i++) {
            for (int j = 0; j < group[i].size(); j++) {
                cout << "group[" << i << "][" << j << "]: " << group[i][j] << " ";
            }
            cout << endl;
        }
        cout << "bicorable_group: " << endl;
        auto bicolor_group = query_bicorable_group();
        for (int i = 0; i < bicolor_group.size(); i++)
            cout << "bicolor_group[" << i << "]: " << bicolor_group[i] << endl;
        cout << "monochromatic_dominating_set: " << query_monochromatic_dominating_set() << endl;
    }
};

BicolorGraph graph;

template<typename T>
struct Warshall {
    int n;
    vector<vector<T>> dist;
    vector<vector<int>> route;
    T inf = numeric_limits<T>::max();
    void init(int n) {
        this->n = n;
        dist.resize(n);
        for (int i = 0; i < n; i++)
            dist[i].assign(n, inf);
        route.resize(n);
        for (int i = 0; i < n; i++)
            route[i].assign(n, -1);
        for (int i = 0; i < n; i++) {
            dist[i][i] = 0;
            route[i][i] = i;
        }
    }
    void update(int s, int t, T w) {
        if (dist[s][t] > w) {
            dist[s][t]  = w;
            route[s][t] = t;
        }
    }
    void source() {
        for (int k = 0; k < n; k++) {
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    if (dist[i][k] < inf && dist[k][j] < inf) {
                        if (dist[i][k] + dist[k][j] < dist[i][j]) {
                            dist[i][j] = dist[i][k] + dist[k][j];
                            route[i][j] = route[i][k];
                        }
                    }
                }
            }
        }
    }
    T query_dist(int s, int t) {
        return dist[s][t];
    }
    vector<int> query_route(int s, int t) {
        vector<int> path;
        if (route[s][t] != -1) {
            path.push_back(s);
            while (s != t) {
                s = route[s][t];
                path.push_back(s);
            }
        }
        return path;
    }
    void dump() {
        for (int i = 0; i < n; i++)
            cout << "dist[" << i << "]: " << dist[i] << endl;
        for (int i = 0; i < n; i++)
            cout << "route[" << i << "]: " << route[i] << endl;
    }
};

Warshall<int> warshall;

int main() {
    ios::sync_with_stdio(false);
    int n;
    cin >> n;
    vector<vector<int>> W(n, vector<int>(n));
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            char ch;
            cin >> ch;
            W[i][j] = ch - '0';
        }
    }
    // cout << "n: " << n << endl;
    // cout << "W: " << W << endl;
    graph.init(n);
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            if (W[i][j]) {
                graph.add_undirected_edge(i, j);
            }
        }
    }
    graph.refactor_edge();
    // graph.dump_edge();
    graph.source_bicolor();
    if (graph.query_is_bicolorable()) {
        warshall.init(n);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (W[i][j]) {
                    warshall.update(i, j, 1);
                }
            }
        }
        warshall.source();
        int max_val = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                int val = warshall.query_dist(i, j);
                if (val != warshall.inf) {
                    max_val = max(max_val, val);
                }
            }
        }
        cout << max_val + 1 << endl;
    } else {
        cout << -1 << endl;
    }
    return 0;
}


Measured memory_bytes: 1024