// verified at https://codeforces.com/contest/1290/submission/70120095
#ifndef Bipartite_union_find
#define Bipartite_union_find

#include <cassert>
#include <cstddef>
#include <utility>

class bipartite_union_find
{
    class node
    {
        node *link = nullptr;
        bool mark = 0;
        size_t cnt[2] = {1, 0};
    public:
        size_t size() const { return cnt[0] + cnt[1]; }
        size_t size(const bool color) const { return cnt[color]; }
        node *find()
        {
            if(!link) return this;
            node *const root = link->find();
            mark ^= link->mark;
            return link = root;
        }
        bool color() { return find(), mark; }
        void merge(node *other, const bool flip)
        {
            other->link = this, other->mark = flip;
            for(bool i : {0, 1}) cnt[i] += other->cnt[i ^ flip];
        }
    };

    const size_t n;
    node *const tree;

    bool unite(node *x, node *y, bool diff_color)
    {
        diff_color ^= x->color() ^ y->color();
        x = x->find(), y = y->find();
        if(x == y) return !diff_color;
        if(x->size() < y->size()) std::swap(x, y);
        x->merge(y, diff_color);
        return true;
    }

public:
    explicit bipartite_union_find(const size_t _n) : n(_n), tree(new node[n]) {}
    ~bipartite_union_find() { delete[] tree; }

    size_t find(const size_t x)
    {
        assert(x < n);
        return tree[x].find() - tree;
    }

    size_t size() const { return n; }

    size_t size(const size_t x)
    {
        assert(x < n);
        return tree[x].find()->size();
    }

    size_t size(const size_t x, const bool color)
    {
        assert(x < n);
        return tree[x].find()->size(color);
    }

    bool color(const size_t x)
    {
        assert(x < n);
        return tree[x].color();
    }

    bool same(const size_t x, const size_t y)
    {
        assert(x < n && y < n);
        return tree[x].find() == tree[y].find();
    }

    // return if the condition given is possible.
    bool unite(const size_t x, const size_t y, const bool diff_color)
    {
        assert(x < n && y < n);
        return unite(tree + x, tree + y, diff_color);
    }
}; // class bipartite_union_find

#endif // Bipartite_union_find

#include <iostream>
#include <vector>
using namespace std;

int main(int argc, char *argv[])
{
    std::ios::sync_with_stdio(false), std::cin.tie(nullptr);

    int n;
    cin >> n;
    vector<string> g(n);
    for(auto &s:g) cin>>s;

    int dist[222][222];
    for(int i=0; i<n; ++i)
        for(int j=0; j<n; ++j)
            dist[i][j]=n;

    bipartite_union_find uf(n);
    for(int i = 0; i < n; ++i)
    {
        for(int j = 0; j < n; ++j)
        {
            if(g[i][j] - '0')
            {
                if(!uf.unite(i,j,1))
                {
                    cout << -1 << "\n";
                    return 0;
                }
                dist[i][j] = 1;
            }
            else
            {
                if(i==j)
                {
                    dist[i][j]=0;
                }
            }
        }
    }
    for(int k = 0; k < n; ++k)
    {
        for(int i = 0; i < n; ++i)
        {
            for(int j = 0; j < n; ++j)
            {
                dist[i][j]=min(dist[i][j], dist[i][k] + dist[k][j]);
            }
        }
    }

    int ans=0;
    for(int i = 0; i < n; ++i)
    {
        vector<int> comp;
        for(int j = 0; j < n; ++j)
        {
            if(uf.find(j)==i)
            {
                comp.emplace_back(j);
            }
        }
        int k=0;
        for(int a : comp)
        {
            for(int b : comp)
            {
                if(k<=dist[a][b])
                {
                    k=dist[a][b]+1;
                }
            }
        }
        ans+=k;
    }
    cout << ans << "\n";
}


Measured memory_bytes: 512